[프로그래머스 코딩테스트 파이썬] 과제 진행하기

위정우·2023년 4월 6일
0
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으로 시작하지 않습니다.
      • 배열은 시간순으로 정렬되어 있지 않을 수 있습니다.
  • 진행중이던 과제가 끝나는 시각과 새로운 과제를 시작해야하는 시각이 같은 경우 진행중이던 과제는 끝난 것으로 판단합니다.

레벨2 문제를 처음 풀어봤는데 정~말 어려웠다.
사람들 얘기로는 스택구조로 풀어야 한다는데 파이썬에서의 스택을 잘 몰라서... 내 나름대로의 방법으로 풀어보았다.
가장 어려웠던 부분은 테스트 케이스에서 시간 초과가 큰 문제였다.
그래서 반복문이 가득하던 코드에서 최대한 반복문을 줄였다.

import copy # deepcopy를 사용하기 위한 모듈
def solution(plans):
    plans.sort(key=lambda x:x[1]) # 과제 시작 시간 기준으로 오름차순으로 정렬
    answer = []
    starttime = [] # 과제 시작 시간
    time = [int(i[2]) for i in plans] # 과제 수행 남은 시간
    c = 0 # 과제를 선택할 인덱스 카운트
    for i in plans: # 각 과목의 시작 시간을 분으로 치환하여 저장(하루 1440분)
        spl = i[1].split(':')
        starttime.append(int(spl[0])*60 + int(spl[1]))
        
    realtime = copy.deepcopy(starttime[0]) # 현재 시간으로 사용할 변수(copy 모듈의 deepcopy를 사용하지 않으면 starttime[0]이 전역으로 바라보고 있어서 starttime 을 바꾸면 realtime 도 바뀌게 된다.)

    while c < len(plans):
        if time[c] > 0: # 반복문이 돌 때마다 남은 과제 수행시간이 1분씩 줄어듦
            time[c] -= 1
        realtime += 1 # 반복문이 돌 때마다 실제 시간이 1분씩 늘어남
        
        if time[c] == 0 and plans[c][0] not in answer: # 현재 과제의 남은 진행시간이 0분이고, answer에 들어가있지 않다면 answer에 해당 과제 추가
            answer.append(plans[c][0])
            if realtime in starttime: # 현재 시간이 각 과제 시작 시간 리스트에 포함돼있다면 과제 인덱스(c)를 해당 과제 인덱스로 변경(plans, starttime, time 각 리스트는 같은 조건으로 정렬됐기 때문에 같은 인덱스끼리 같은 것)
                c = starttime.index(realtime)
                
            else:
                for i in reversed(range(len(time))): # 위 조건문이 아니라면 이전에 수행했던 과제들중 과제 수행 남은 시간이 0이 아닌 가장 높은 인덱스로 과제 인덱스(c)를 전환. 
                    if time[i] != 0 and c > i:
                        c = i
                        break
            continue # c값을 건드린 후로는 밑의 코드를 진행하면 안되기 때문에 continue

        if realtime in starttime: # 남은 진행시간이 남았지만 다음 과제 시작 시간이 됐을경우 해당 과제로 인덱스(c) 변경
                c = starttime.index(realtime)
        if c == len(plans): # 인덱스 초과시 반복문 중단
            break
        if len(answer) == len(plans): # answer에 모든 과제가 들어가면 반복문 중단
            break        

    return answer

copy 모듈의 경우 파이썬에서 기본적으로 제공하는 모듈이기 때문에 코테에서 사용해도 문제가 없을것이라고 한다.

풀이 방향성

  1. 실제 시간이 1씩 증가함에 따라 현재 진행하는 과제의 남은 시간을 1씩 줄인다.
  1. 과제의 남은 시간이 0이라면 answer에 해당 과제를 추가하고, 조건문으로 현재 시간이 다른 과제의 시작시간과 일치한다면 일치하는 과제로 넘어가고, 아니라면 이전에 수행했던 과목중(reverse로 높은 인덱스부터 탐색) 과제 남은 시간이 0이 아닌 과목으로 넘어간다.
  1. 위 2번에 해당하지 않을 때(아직 과제 남은시간이 0이 아닌데 다음 과제 시작 시간과 현재 시간이 일치하게 될 경우) 해당 과목으로 넘어간다.
  1. 인덱스가 범위를 초과하는 경우 break로 반복문 중단.

처음엔 반복문이 많아서 시간 초과로 테스트에 성공하지 못했지만 최대한 반복문을 줄이는데 성공해 통과했다ㅠㅠ

profile
열심히 하자!

0개의 댓글