프로그래머스 Lv.2 과제 진행하기

Wonkyun Jung·2023년 6월 12일
0

알고리즘

목록 보기
56/59
post-thumbnail

문제 설명

과제를 받은 루는 다음과 같은 순서대로 과제를 하려고 계획을 세웠습니다.

과제는 시작하기로 한 시각이 되면 시작합니다.

새로운 과제를 시작할 시각이 되었을 때, 기존에 진행 중이던 과제가 있다면 진행 중이던 과제를 멈추고 새로운 과제를 시작합니다.

진행중이던 과제를 끝냈을 때, 잠시 멈춘 과제가 있다면, 멈춰둔 과제를 이어서 진행합니다.

만약, 과제를 끝낸 시각에 새로 시작해야 되는 과제와 잠시 멈춰둔 과제가 모두 있다면, 새로 시작해야 하는 과제부터 진행합니다.

멈춰둔 과제가 여러 개일 경우, 가장 최근에 멈춘 과제부터 시작합니다.

과제 계획을 담은 이차원 문자열 배열 plans가 매개변수로 주어질 때, 과제를 끝낸 순서대로 이름을 배열에 담아 return 하는 solution 함수를 완성해주세요.



제한사항

  • 3 ≤ plans의 길이 ≤ 1,000
  • plans의 원소는 [name, start, playtime]의 구조로 이루어져 있습니다.
  • name : 과제의 이름을 의미합니다.
  • 2 ≤ name의 길이 ≤ 10
  • name은 알파벳 소문자로만 이루어져 있습니다.
  • name이 중복되는 원소는 없습니다.
  • start : 과제의 시작 시각을 나타냅니다.
  • "hh:mm"의 형태로 "00:00" ~ "23:59" 사이의 시간값만 들어가 있습니다.
  • 모든 과제의 시작 시각은 달라서 겹칠 일이 없습니다.
  • 과제는 "00:00" ... "23:59" 순으로 시작하면 됩니다. 즉, 시와 분의 값이 작을수록 더 빨리 시작한 과제입니다.
  • playtime : 과제를 마치는데 걸리는 시간을 의미하며, 단위는 분입니다.
  • 1 ≤ playtime ≤ 100
  • playtime은 0으로 시작하지 않습니다.
  • 배열은 시간순으로 정렬되어 있지 않을 수 있습니다.
  • 진행중이던 과제가 끝나는 시각과 새로운 과제를 시작해야하는 시각이 같은 경우 진행중이던 과제는 끝난 것으로 판단합니다.


입출력 예



정답코드

//1. 시간이 빠른 순으로 진행해야 하니까 시간을 ":"을 기준으로 split 해서 분으로 바꿔서 sort
//2. 가장 최근에 멈춘 과제부터 다시 시작하니까 LIFO Stack을 사용한다
//3. 현재 과제를 하면서 그 다음 과제의 시작시간이 언제인지 tracking 한다. 
//4. 완료가 된 순서대로 ArrayList에 넣는다 

import java.util.*;

class homework implements Comparable<homework> {
    
    String name;
    int startTime;
    int time;
    
    public homework(String name, int startTime, int time){
        this.name = name;
        this.startTime = startTime;
        this.time = time;
    }
    
    public int compareTo(homework o){
        return this.startTime - o.startTime;
    }
}

class rest {
    
    String name;
    int restTime;
    
    public rest(String name, int restTime){
        this.name = name;
        this.restTime = restTime;
    }
}

class Solution {
    public String[] solution(String[][] plans) {
        String[] answer = {};
        
        PriorityQueue<homework>pq = new PriorityQueue<>();
        ArrayList<String>result = new ArrayList<>();
        Stack<rest>restStack = new Stack<>();
        
        
        for(String [] plan : plans){
            String name = plan[0];
            int time = Integer.parseInt(plan[2]);
            
            String [] temp = plan[1].split(":");
            int startTime = Integer.parseInt(temp[0])*60 + Integer.parseInt(temp[1]);
            pq.offer(new homework(name, startTime, time));
        }
        
        
        int nextStart = 0;
        int nowEnd, diff;
        
        while(!(pq.isEmpty()&&restStack.isEmpty())){
            
            //List에 할 일이 남아있을 때
            if(!pq.isEmpty()){
                homework now = pq.poll();
                nowEnd = now.startTime+now.time;
                
                if(!pq.isEmpty())nextStart = pq.peek().startTime;
                else if(pq.isEmpty()){
                    result.add(now.name);
                    continue;
                }
                
                //지금 하는 과제 중간에 다른 과제를 시작해야한다면 stack에 남은시간과 함께 추가하기 
                if(nowEnd > nextStart){
                    diff = nowEnd - nextStart;
                    rest task = new rest(now.name,diff);
                    restStack.push(task);
                }
                
                //지금하는 일을 다음 과제 시작전에 마칠 수 있을 때 
                else{ 
                    result.add(now.name);
                    if(restStack.isEmpty()||nowEnd == nextStart||pq.isEmpty())continue;
                    
                    //과제를 하고 남은 시간 
                    diff = nextStart - nowEnd;
                    
                    //남은 시간을 다 써도 stack 첫번째 일을 못 끝내는 경우
                    if(diff < restStack.peek().restTime){
                        restStack.peek().restTime-=diff;
                    }
                    
                    //남은 시간 동안 1개 이상의 과제를 할 수 있을 때
                    else{                   
                        //과제를 모두 끝낼 수 있을 때 까지 stack에서 꺼내 과제를 한다
                        while (diff > 0){
                            if(restStack.isEmpty())break;
                            rest nowRest = restStack.pop();
                            
                            //하나를 끝내고 이 다음 과제도 수행이 가능하면
                            if(diff - nowRest.restTime >= 0){
                                diff -= nowRest.restTime;
                                result.add(nowRest.name);
                                continue;
                            }
                            
                            //이번 과제를 모두 끝내지 못하는 경우 
                            if(diff - nowRest.restTime < 0){
                                rest addRest = new rest(nowRest.name,nowRest.restTime-diff);
                                restStack.push(addRest);
                                break;
                            }
                        }
                    }  
                }
            }
            
            //List에 할 일이 없을 때 stack에 남은 일들을 처리 
            else{
                while(!restStack.isEmpty()){
                    rest nowRest = restStack.pop();
                    result.add(nowRest.name);
                }
            }
        }
        
        answer = new String[result.size()];
        for(int i = 0; i < result.size(); i++){
            answer[i] = result.get(i);
        }
        
        return answer;
    }
}


피드백

문제 자체는 쉬움(딱히 큰 알고리즘이 있는 건 아님) 하지만 구현 난이도가 높고 자료구조를 잘 활용해야하는 문제. 실수 없이 잘 훑어보자. 난이도 6/10, 걸린시간 1시간 30분

0개의 댓글