devFancy BE Developer

[Programmers] 176962. 과제 진행하기

2023-11-03
devFancy

문제 링크

성능 요약

  • 메모리: 84.5 MB, 시간: 6.34 ms

구분

  • 코딩테스트 연습 > 연습문제 > 우선순위 큐, 스택

Answer Code1(23.11.03)

import java.util.*;

class Solution {
    
    static class Task {
        private String name;
        private int start;
        private int playtime;
        
        public Task(String name, int start, int playtime) {
            this.name = name;
            this.start = start;
            this.playtime = playtime;
        }
        
        public Task(String name, int playtime) {
            this.name = name;
            this.playtime = playtime;
        }
    }
    
    public List<String> solution(String[][] plans) {
    	// 정답을 저장할 리스트
        List<String> answer = new ArrayList<>();
        
        // 해야할 과제들을 시작시간 순으로 저장
        PriorityQueue<Task> pq = new PriorityQueue<>(
            (o1, o2) -> (o1.start - o2.start)
        );
        
        for(int i = 0; i < plans.length; i++) {
            String name = plans[i][0];
            
            String[] str = plans[i][1].split(":");
            int h = Integer.parseInt(str[0]);
            int m = Integer.parseInt(str[1]);
            int start = (h * 60) + m; // '분' 기준으로 합침
            
            int time = Integer.parseInt(plans[i][2]);
            
            pq.add(new Task(name, start, time));
        }
        
        // 잠시 멈춘 과제를 저장
        Stack<Task> remainingTasks = new Stack<>();
        
        while(!pq.isEmpty()) {
            Task currentTask = pq.poll();
            
            String curName = currentTask.name;
            int curStart = currentTask.start;
            int curPlaytime = currentTask.playtime;
            
            // 현재 시각
            int currentTime = curStart;
            
            // 1) 새로운 과제가 남아있는 경우(진행중이던 과제 제외)
            if(!pq.isEmpty()) {
                Task nextTask = pq.peek();
                
                // 1-1) 지금 과제를 끝내고도 다음 과제 시작까지 시간이 남는 경우
                if(currentTime + curPlaytime < nextTask.start) {
                    answer.add(curName);
                    currentTime += curPlaytime;
                    
                    // 잠시 멈춘 과제가 있는 경우, 남는 시간동안 멈췄던 과제 해결
                    while(!remainingTasks.isEmpty()) {
                        Task rem = remainingTasks.pop();
                        
                        // 다음 새로운 과제 시작전까지 다 끝낼수 있는 경우
                        if(currentTime + rem.playtime <= nextTask.start) {
                            currentTime += rem.playtime;
                            answer.add(rem.name);
                            continue;
                        } 
                        // 다음 새로운 과제 시작전까지 못 끝내는 경우
                        else {
                            int t = rem.playtime - (nextTask.start - currentTime);
                            // 추가로 한 시간만 빼서 멈춘 과제 목록에 다시 추가
                            remainingTasks.push(new Task(rem.name, t));
                            break;
                        }
                    }
                }
                // 1-2) 지금 과제 끝내면 새로운 과제 시작할 시간인 경우
                else if(curStart + curPlaytime == nextTask.start) {
                    answer.add(curName);
                    continue;
                }
                // 1-3) 새로운 과제 시작전까지 지금 과제를 못 끝내는 경우
                else {
                    int t = (nextTask.start - currentTime);
                    remainingTasks.push(new Task(curName, curPlaytime - t));
                }
                
            }
            
            // 2) 더 이상 남아있는 새로운 과제가 없는 경우
            else {
                // 2-1) 남아있는 과제(잠시 멈춘 과제)도 없는 경우
                if(remainingTasks.isEmpty()) {
                    currentTime += curPlaytime;
                    answer.add(curName);
                }
                // 2-2) 남아있는 과제는 있는 경우
                else {
                    answer.add(curName); // 새로운 과제부터 먼저 해결
                    
                    // 남아있는 과제들을 정해진 순서대로 끝내면 됨
                    while(!remainingTasks.isEmpty()) {
                        Task rem = remainingTasks.pop();
                        answer.add(rem.name);
                    }
                }
            }
        }
        
        return answer;
    }
}

문제풀이

과제를 진행할 때 과제의 시작 시각(start)을 기준으로 하므로 PriorityQueue를 사용해서 과제들의 정보를 시작 시간 순으로 저장합니다.

그리고 멈춰둔 과제가 여러 개 있는 경우 가장 최근에 멈춘 과제부터 시작한다는 점에서 멈춘 과제들을 저장하기 위해 Stack을 사용했습니다.

새로운 과제와 잠시 멈춘 과제를 고려해서 여러 경우의 수를 생각하고, 각각의 상황에 따라 적합한 코드를 작성하여 문제를 해결하는 것이였습니다.

Review

구현에 대해 설명은 자세하나 그 속은 복잡한 문제였다.

경우의 수가 많고, 그에 따라 문제를 푸는데 헷갈려서 아이패드를 이용했다.

해당 문제를 나중에 다시 복습하면서, 우선순위 큐와 스택을 활용하는 것에 익숙해지자.


Comments

Index