퍼즐 게임 챌린지(L2)
- 이진 탐색
문제 설명
당신은 순서대로 n
개의 퍼즐을 제한 시간 내에 풀어야 하는 퍼즐 게임을 하고 있습니다. 각 퍼즐은 난이도와 소요 시간이 정해져 있습니다. 당신의 숙련도에 따라 퍼즐을 풀 때 틀리는 횟수가 바뀌게 됩니다. 현재 퍼즐의 난이도를 diff
, 현재 퍼즐의 소요 시간을 time_cur
, 이전 퍼즐의 소요 시간을 time_prev
, 당신의 숙련도를 level
이라 하면, 게임은 다음과 같이 진행됩니다.
diff
≤level
이면 퍼즐을 틀리지 않고time_cur
만큼의 시간을 사용하여 해결합니다.diff
>level
이면, 퍼즐을 총diff
-level
번 틀립니다. 퍼즐을 틀릴 때마다,time_cur
만큼의 시간을 사용하며, 추가로time_prev
만큼의 시간을 사용해 이전 퍼즐을 다시 풀고 와야 합니다. 이전 퍼즐을 다시 풀 때는 이전 퍼즐의 난이도에 상관없이 틀리지 않습니다.diff
-level
번 틀린 이후에 다시 퍼즐을 풀면time_cur
만큼의 시간을 사용하여 퍼즐을 해결합니다.
예를 들어 diff
= 3, time_cur
= 2, time_prev
= 4인 경우, level
에 따라 퍼즐을 푸는데 걸리는 시간은 다음과 같습니다.
level
= 1이면, 퍼즐을 3 - 1 = 2번 틀립니다. 한 번 틀릴 때마다 2 + 4 = 6의 시간을 사용하고, 다시 퍼즐을 푸는 데 2의 시간을 사용하므로 총 6 × 2 + 2 = 14의 시간을 사용하게 됩니다.level
= 2이면, 퍼즐을 3 - 2 = 1번 틀리므로, 6 + 2 = 8의 시간을 사용하게 됩니다.level
≥ 3이면 퍼즐을 틀리지 않으며, 2의 시간을 사용하게 됩니다.
퍼즐 게임에는 전체 제한 시간 limit
가 정해져 있습니다. 제한 시간 내에 퍼즐을 모두 해결하기 위한 숙련도의 최솟값을 구하려고 합니다. 난이도, 소요 시간은 모두 양의 정수며, 숙련도도 양의 정수여야 합니다.
퍼즐의 난이도를 순서대로 담은 1차원 정수 배열 diffs
, 퍼즐의 소요 시간을 순서대로 담은 1차원 정수 배열 times
, 전체 제한 시간 limit
이 매개변수로 주어집니다. 제한 시간 내에 퍼즐을 모두 해결하기 위한 숙련도의 최솟값을 정수로 return 하도록 solution 함수를 완성해 주세요.
제한사항
- 1 ≤
diffs
의 길이 =times
의 길이 =n
≤ 300,000diffs[i]
는i
번째 퍼즐의 난이도,times[i]
는i
번째 퍼즐의 소요 시간입니다.diffs[0]
= 1- 1 ≤
diffs[i]
≤ 100,000 - 1 ≤
times[i]
≤ 10,000
1 ≤
limit
≤ 1015
- 제한 시간 내에 퍼즐을 모두 해결할 수 있는 경우만 입력으로 주어집니다.
입출력 예
diffs | times | limit | result |
---|---|---|---|
[1, 5, 3] | [2, 4, 7] | 30 | 3 |
[1, 4, 4, 2] | [6, 3, 8, 2] | 59 | 2 |
[1, 328, 467, 209, 54] | [2, 7, 1, 4, 3] | 1723 | 294 |
[1, 99999, 100000, 99995] | [9999, 9001, 9999, 9001] | 3456789012 | 39354 |
입출력 예 설명
입출력 예 #1
숙련도가 3인 경우 다음과 같이 진행됩니다.
- 1번째 퍼즐을 2의 시간을 사용하여 해결합니다.
- 2번째 퍼즐을 5 - 3 = 2번 틀려서 총 (4 + 2) × 2 + 4 = 16의 시간을 사용하여 해결합니다.
- 3번째 퍼즐을 7의 시간을 사용하여 해결합니다.
총 2 + 16 + 7 = 25의 시간을 사용하여 모든 퍼즐을 해결할 수 있습니다. 숙련도가 3보다 작은 경우 제한 시간인 30 이내에 모든 퍼즐을 해결할 수 없습니다.
따라서 3을 return 해야 합니다.
입출력 예 #2
숙련도가 2인 경우 다음과 같이 진행됩니다.
- 1번째 퍼즐을 6의 시간을 사용하여 해결합니다.
- 2번째 퍼즐을 4 - 2 = 2번 틀려서 총 (3 + 6) × 2 + 3 = 21의 시간을 사용하여 해결합니다.
- 3번째 퍼즐을 4 - 2 = 2번 틀려서 총 (8 + 3) × 2 + 8 = 30의 시간을 사용하여 해결합니다.
- 4번째 퍼즐을 2의 시간을 사용하여 해결합니다.
총 6 + 21 + 30 + 2 = 59의 시간을 사용하여 모든 퍼즐을 해결할 수 있습니다. 숙련도가 2보다 작은 경우 제한 시간인 59 이내에 모든 퍼즐을 해결할 수 없습니다.
따라서 2를 return 해야 합니다.
입출력 예 #3
숙련도가 294인 경우 다음과 같이 진행됩니다.
- 1번째 퍼즐을 2의 시간을 사용하여 해결합니다.
- 2번째 퍼즐을 328 - 294 = 34번 틀려서 총 (7 + 2) × 34 + 7 = 313의 시간을 사용하여 해결합니다.
- 3번째 퍼즐을 467 - 294 = 173번 틀려서 총 (1 + 7) × 173 + 1 = 1385의 시간을 사용하여 해결합니다.
- 4번째 퍼즐을 4의 시간을 사용하여 해결합니다.
- 5번째 퍼즐을 3의 시간을 사용하여 해결합니다.
총 2 + 313 + 1385 + 4 + 3 = 1707의 시간을 사용하여 모든 퍼즐을 해결할 수 있습니다. 숙련도가 294보다 작은 경우 제한 시간인 1723 이내에 모든 퍼즐을 해결할 수 없습니다.
따라서 294를 return 해야 합니다.
입출력 예 #4
숙련도가 39354인 경우 다음과 같이 진행됩니다.
- 1번째 퍼즐을 9999의 시간을 사용하여 해결합니다.
- 2번째 퍼즐을 99999 - 39354 = 60645번 틀려서 총 (9001 + 9999) × 60645 + 9001 = 1152264001의 시간을 사용하여 해결합니다.
- 3번째 퍼즐을 100000 - 39354 = 60646번 틀려서 총 (9999 + 9001) × 60646 + 9999 = 1152283999의 시간을 사용하여 해결합니다.
- 4번째 퍼즐을 99995 - 39354 = 60641번 틀려서 총 (9001 + 9999) × 60641 + 9001 = 1152188001의 시간을 사용하여 해결합니다.
총 9999 + 1152264001 + 1152283999 + 1152188001 = 3456746000의 시간을 사용하여 모든 퍼즐을 해결할 수 있습니다. 숙련도가 39354보다 작은 경우 제한 시간인 3456789012 이내에 모든 퍼즐을 해결할 수 없습니다.
따라서 39354를 return 해야 합니다.
나의 풀이 (오답)
1
2
3
4
5
6
7
8
9
10
11
12
13
def solution(diffs, times, limit):
answer = 0
level=1
while True:
total_time=sum(times)+times[0]*max(0,diffs[0]-level)
for i in range(1,len(times)):
it=max(0,diffs[i]-level)
total_time+=((times[i]+times[i-1])*it)
if total_time<=limit:
answer=level
break
level+=1
return answer
- level 1부터 시작해서 가능한 최소값이 나올 때까지 모두 구하도록 코딩하였다.
- 시간 초과가 발생한다. (diffs는 100,000의 길이를 갖기 때문)
- 따라서 이진 탐색을 사용해서 문제를 풀어야 한다.
나의 풀이 (정답)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def solution(diffs, times, limit):
min_l,max_l=1,100000
while min_l<max_l:
mid_l=(min_l+max_l)//2
total_time=sum(times)+times[0]*max(0,diffs[0]-mid_l)
for i in range(1,len(times)):
it=max(0,diffs[i]-mid_l)
total_time+=((times[i]+times[i-1])*it)
if total_time>limit:
break
if total_time<=limit:
max_l=mid_l
else:
min_l=mid_l+1
answer=max_l
return answer
- 최대, 최소 level이 100000, 1이므로 설정해두고, 중앙값을 현재 level이라고 둔다.
- 중앙값이 limit를 넘지 않으며 만족할 때, 더 작은 level값이 존재할 수도 있으므로 max_l을 mid_l값으로 설정해준다. (mid_l값 자체가 정답일 수도 있기 때문)
- limit를 넘으면 mid_l+1값을 min_l값에 넣어준다. (mid_l값은 정답일 수가 없다)
- 최종적으로 min_l==max_l이 만족하면 while문이 멈춘다.