프로그래머스 과제 진행하기
프로그래머스 과제 진행하기 — #프로그래머스 #프로그래머스과제진행하기 #개발자의도구들 사용된 언어: 코틀린, 혹은 파이썬 순서: 로직,...
#프로그래머스#Naver Blog
#프로그래머스 #프로그래머스과제진행하기 #개발자의도구들
- 사용된 언어: 코틀린, 혹은 파이썬
- 순서: 로직, 코드 구현, 코드 분석
아이디어
Programmers 과제진행하기(연습문제 정답율 41%)
- 문제의도
- 시간 데이터 처리
- 적절한 자료구조 결정
- Stack
🙅 제약조건 정리
1. play time 값 1분~100분
2. 과제 갯수 1~1000개
3. 새로운 과제 > 진행중 과제
4. 진행중 이던 과제와 새로운 과제 시작시간이 같다 -> 진행중 과제가 끝난 것
5. 과제를 끝낸 순서대로 이름을 담아서 return
6. 멈춘과제가 여러개 -> 가장 최근 과제부터 진행 -> stack
⌛ 시간 데이터 관리
>>> 파이썬에서 변환 ?
1. "12:30" -> 1230 -> 숫자로 표현해도 무방함
2. 시간 올리기
>>> 100으로 나눈 나머지
💡 시간의 흐름 계산
1. 1분씩 증가시키기
>>> 단순하다.
>>> 비용이 많이 발생한다.
2. 수식으로 표현하기
>>> 수식 찾기 어려워
>>> 비용을 줄일 수 있다.
필요한 method
1. 문자열 시계를 수로 변환하는 메서드
2. 문자열을 숫자로 변환 -> 기본 내장
🔑 로직 정리
1. 초기셋팅
>>> 문자열 시간 데이터를 int로 변경
>>> playtime을 int로 변경
>>> plans를 시간 데이터 기준으로 정렬(오름차순)
>>> 초기 시간: plans[0][1]
>>> pause = 멈춤 job stack
>>> complete = 끝낸 job list(queue) anyway
2. 시간 비교시 고려사항
>>> 현재 시간 기준으로
>>> 현재 job이 끝났는지
- 끝났다면 -> pause값이 비어있는지
--- 비어잇다 : 다음 job 시간 확인
--- 비어있지 않다: pause에서 pop하여 current_job을 업데이트한다.
1차시도
def solution(plans):
answer = []
print(int("20"))
# O(n)
for plan in plans:
time = plan[1].replace(":", "")
plan[1] = int(time)
plan[2] = int(plan[2])
# O(nlogn)
plans.sort(key=lambda plan: plan[1])
print(plans)
current = [0, plans[0][1], plans[0][2]] #idx
pause = []
complete = []
while 1:
if len(complete) == len(plans):
print("compltet!!")
break
print("working start!!\n>>>")
# check current job finish?
if current[2] > 0: # not finish
time = current[1] + 1
print("time up +1 ", time)
next_idx = current[0] + 1
print("next_idx", next_idx)
# check start new?
if next_idx < len(plans) and time >= plans[next_idx][1]:
print("newt job start!")
# start new
pause.append(current)
print("after add current job: pause: ", pause)
current = [next_idx, plans[next_idx][1], plans[next_idx][2]]
print("current update", current)
# current = [idx, working time, left time]
left_time = current.pop()
working_time = current.pop() + 1
print("current: left_time, working_time: ", left_time, working_time)
# adjust time
if working_time % 100 >= 60:
working_time -= 60
working_time += 100
if working_time >= 2400:
working_time -= 2400
current.append(working_time)
current.append(left_time - 1)
print("update current!", current)
else: # if finsish
print("current is finish !", current)
complete.append(current[0]) # only idx
print("add complete!", complete)
# check in pause
if pause:
current = pause.pop()
print("puase has jobs: and current update!", pause, '->', current)
else:
print("there is no pause!")
next_idx = current[0] + 1
if next_idx < len(plans):
next_job = plans[next_idx]
current = [next_idx, next_job[1], next_job[2]]
print("next_job is set!", current)
else:
print("there isn't another job, plans is empty")
print("\n")
print("logic finish! complete: ", complete)
for c in complete:
answer.append(plans[c][0])
return answer
- 코드가 너무 난잡하다
- 시간 관리 로직이 복잡하다 -> 의도치 않게 변경될 수 있다.
- branch가 너무 많아서 관리하기가 어렵다.
앞으로 이런 느낌으로 코드를 작성할거면, 다시 코드 작성을 고려하자.
더 간단하게 생각하기
with gpt\_o3
🤔 시간 관리를 더 쉬우면서도 정확하게 하기
-> 모든 시간을 분으로 관리하자
🤔 조건을 명확하게 정하기 (이건 내가 정리한 로직)
- 1분씩 증가 x
- 시작시간은 처음 사용 이후 사용하지 않아도 된다.
- 체크사항:
>>> next job이 있는지?
>>> pause job이 있는지?
def time_to_minute(time: str):
hh, mm = map(int, time.split(":"))
return hh * 60 + mm
def solution(plans):
# plans의 각 요소: [name, start, playtime]
# 시작 시각을 분 단위로 변환, playtime은 int
for plan in plans:
plan[1] = time_to_minute(plan[1])
plan[2] = int(plan[2])
# 시작 시각 오름차순 정렬
plans.sort(key=lambda x: x[1])
complete = [] # 완료된 과제의 이름을 순서대로 저장
paused = [] # (과제 이름, 남은 시간)을 저장하는 스택 (LIFO)
current_time = plans[0][1]
# 현재 진행 중인 과제: (name, remaining_time)
current_task = (plans[0][0], plans[0][2])
i = 1 # 다음 과제의 인덱스
while current_task is not None:
# 다음 과제가 존재하는 경우
if i < len(plans):
next_start = plans[i][1]
finish_time = current_time + current_task[1]
# 만약 현재 과제를 finish하기 전에 다음 과제 시작 시각이 도래한다면
if finish_time > next_start:
# 현재 과제를 next_start까지 진행하고 중단
dt = next_start - current_time # 진행한 시간
# 남은 시간 갱신
current_task = (current_task[0], current_task[1] - dt)
# 현재 시간 업데이트
current_time = next_start
# 진행 중이던 과제를 멈추고 스택에 저장
paused.append(current_task)
# 새 과제를 시작
current_task = (plans[i][0], plans[i][2])
i += 1
else:
# 현재 과제를 finish 할 수 있음 (finish_time <= next_start)
current_time = finish_time
complete.append(current_task[0])
# 과제가 끝난 시각에 새 과제 시작 시간이 있다면
# 문제 조건에 따라 새 과제를 우선 시작하므로, pause 스택은 그대로 둡니다.
if i < len(plans) and current_time == plans[i][1]:
current_task = (plans[i][0], plans[i][2])
i += 1
# 아니라면, 만약 멈춰둔 과제가 있다면 가장 최근에 멈춘 과제를 이어서 진행
elif paused:
current_task = paused.pop()
# paused에 과제가 없으면, 다음 과제가 있다면 그 과제를 시작
elif i < len(plans):
# current_time을 다음 과제의 시작 시각으로 이동 (만약 current_time < next_start)
current_time = max(current_time, plans[i][1])
current_task = (plans[i][0], plans[i][2])
i += 1
else:
current_task = None
else:
# 다음 과제가 없으면 현재 과제를 완료하고, 이어서 pause 스택에서 처리
current_time += current_task[1]
complete.append(current_task[0])
if paused:
current_task = paused.pop()
else:
current_task = None
return complete
- 구현이 너무 복잡해서 일단 복붙코드
- 차후 다시 읽어보면서 공부해야 될 것 같다.
플랫폼 변경
이번 문제를 풀면서 꽤나 큰 좌절감을 느꼈다. 솔직히 이번에 느낀건 아직까지 lv2는 나한테 버겁다... 이번 년도 남은 준비기간이 6개월도 안된다. 매번 한다고하는데, 실력이 안는다. 공부시간을 더 투자해야할 것 같다.
찾아보닌깐 하루 1코테 하기에는 LeetCode가 좋다더라. 사실 나도 LeetCode할 때가 가장 좋긴했다. 무엇보다도 문제를 스스로 풀고 성장하고 있다고 느끼는게 공부를 지속하는데 큰 도움이된다. 근데 나는 아직까지 lc2를 정복하지도 못했다. 그냥 이제는 받아들이고 싶다. 나는 이정도 실력이구나...
그냥 LeetCode 쉬운 문제푸터 양치기 식으로 쭉 풀어나가야 겠다. 이런 쉬운 문제도 혼자 버거워 하는게 너무 마음이 안좋다.
다시보기
25.02.21 작성
문제를 풀고 다시 복기해보면서 정확한 논리를 정리하면 좋겠다는 생각이 들었다. 다시 제대로 정리해보자
✅ 1. 모든 시작 시간을 분으로 표시한다.
✅ 2. 모든 play_time을 int로 변경한다.
✅ 3. plasn를 시작 시간 기준으로 정렬한다.
✅ 여러 자료들
>>> current = (name, remain time)
>>> paused = current와 동일
>>> complete = name만 넣기
>>> time = 현재(전체) 시간을 관리하는 int값
🔑 논리
a. 다음 시작 시간이 존재한다.
1. 현재 시간 + 남은 시간 더한다.
>>> 1.1. 다음 시작 시간보다 크다.
>>> 1.2. 다음 시작 시간보다 작다.
1.1. 다음 시작 시간보다 크다.
>>> 현재 시간을 업데이트한다 = 다음 시작시간
>>> 현재 진행 중이던 job을 puased로 넘긴다. -> remain_time을 진행 시간 만큼 차감한다.
>>> 현재 job을 업데이트한다 = 다음 job
1.2. 다음 시작 시간보다 작다.
>>> 현재 일이 끝난 것으로 간주된다.
>>> 새로운 job으로 업데이트 해야한다.
1.2.1. 현재시간을 확인한다.
>>> 다음 시작시간보다 작다
>>>>> paused가 있는지 확인한다.
>>>>> 있으면 현재 job을 puased.pop()으로 업데이트
>>>>> 없으면 다음 job을 현재 job으로 update
b. 다음 시작시간이 존재하지 않는다.
1. 현재일 을 끝낸다.
2. puased를 확인한다
>>> 없다 -> 일이 끝난 것 -> current를 비워 줄 것
>>> 있다 -> pop해서 current로 계속 옮기기
810 / next = 840
마지막 코드 ...
def time_to_minute(time: str):
hh, mm = map(int, time.split(":"))
return hh * 60 + mm
def solution(plans):
complete = [] # name only
for plan in plans:
plan[1] = time_to_minute(plan[1])
plan[2] = int(plan[2])
plans.sort(key=lambda x: x[1])
current = (plans[0][0], plans[0][2]) # name, remain
time = plans[0][1] # start time = 1a minumum of plans start
paused = [] # (name, remain)
i = 1 # next point
print("sorted:", plans)
while current is not None:
print(">>>\n")
if i < len(plans): # do have next?
print("i: ", i)
print("exist next: \ntime:", time, "\ncurrent:", current, "\npaused: ", paused, "\nnext: [", plans[i][0], plans[i][2], "]")
test_time = time + current[1]
if test_time > plans[i][1]:
paused.append((current[0], current[1] -(plans[i][1] - time)))
current = (plans[i][0], plans[i][2])
time = plans[i][1]
i += 1
else:
# current finish
complete.append(current[0])
time = test_time
if time == plans[i][1]:
current = (plans[i][0], plans[i][2])
i += 1
else:
if paused:
current = paused.pop()
else:
current = (plans[i][0], plans[i][2])
i += 1
else: # no have next
print("not exist next", current, paused)
# fininsh current
complete.append(current[0])
if paused:
current = paused.pop()
else:
current = None
print("\n<<<")
return complete
- current finish이후 paused가 없는 경우, 다음 작업이 남아있을 수 있다
- 이를 체크하여 현재 작업을 다음 것으로 동기화 하였다.
- 하지만, 현재 시간은 동기화 하지 않음.
- 이를 동기화 시캬줘야 한다.
🤔 상황
1. 현재 작업이 완료되었다.
2. paused에는 남은 작업이 없다.
3. 다음 작업이 존재한다.
그럼 현재 시간은 항상 = 다음시간으로 set하면된다.
complete.append(current[0])
time = test_time
if i < len(plans) and time == plans[i][1]:
current = (plans[i][0], plans[i][2])
i += 1
elif paused:
current = paused.pop()
elif i < len(plans):
time = plans[i][1]
current = (plans[i][0], plans[i][2])
i += 1
else:
current = None
- 빡통 gpt가 계속 itme = max(time, plans\[i\]\[1\]로 알려줘서 한 30분 날렸다 ...