문제 설명
[본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.]
카카오 초등학교의 "니니즈 친구들"이 "라이언" 선생님과 함께 가을 소풍을 가는 중에 징검다리가 있는 개울을 만나서 건너편으로 건너려고 합니다. "라이언" 선생님은 "니니즈 친구들"이 무사히 징검다리를 건널 수 있도록 다음과 같이 규칙을 만들었습니다.
"니니즈 친구들"은 개울의 왼쪽에 있으며, 개울의 오른쪽 건너편에 도착해야 징검다리를 건넌 것으로 인정합니다.
"니니즈 친구들"은 한 번에 한 명씩 징검다리를 건너야 하며, 한 친구가 징검다리를 모두 건넌 후에 그 다음 친구가 건너기 시작합니다.
디딤돌에 적힌 숫자가 순서대로 담긴 배열 stones와 한 번에 건너뛸 수 있는 디딤돌의 최대 칸수 k가 매개변수로 주어질 때, 최대 몇 명까지 징검다리를 건널 수 있는지 return 하도록 solution 함수를 완성해주세요.
[제한사항]
처음에는 슬라이딩 윈도우를 사용하여 풀고자 했다.
정확도에서는 다 맞았지만, 효율성에서 1개를 빼고 다 시간초과가 났다....
그래서 질문하기에 들어가서 정답을 보진 않고, 문제 힌트만 봤는데, 이진탐색을 사용했다는 제목을 봤다.
이후 바로 다시 문제를 보며 이진탐색으로 어떻게 문제를 풀어볼까 고민하며 문제를 풀기 시작했다.
우선, 가장 중요한 left, right, mid는 징검다리를 건너려는 사람의 수로 정했다.
다음으로 연속적인 돌의 수가 K보다 작은지는 stones배열의 값과 mid를 이용하여 구할 수 있었다.
반복문을 사용해서 stones 배열의 값과 mid의 차이가 0이하라면(stones[i] - mid <= 0),
건너려는 사람의 수보다 돌의 값이 작거나 같아 0이 된다는 의미이고, 우리는 이런 돌이 연속으로 k개 미만이 되는 사람의 수(mid)를 구해야한다.
이를 위해 stones[i] - mid <= 0이 되는 stones 배열의 값이 몇개가 있는지 cnt에 저장한다.
"연속적인 돌의 개수"를 세야하므로, 만약 stones 배열의 값을 하나씩 조사하다가, mid보다 큰 값이 나온다면, 연속이 깨지게 되므로 cnt를 0으로 초기화 해준다.
이러한 cnt가 K보다 크거나 같을때는 mid명의 사람이 건너기 불가능하므로 이진탐색의 탐색 범위를 줄인다.
이후 건너려는 사람의 수보다 작은 값을 가진 연속되는 돌의 개수가 k 이상이면
즉, cnt >= k이면 건너려는 사람의 수가 너무 많은거니까 right = mid - 1을 통해 범위를 조절.
cnt < k이면, 건너려는 사람의 수가 문제의 조건에 맞게 조사됐지만, 최대 값은 아닐 수 있으니
left = mid + 1을 통해 범위 조절.
def solution(stones, k):
left = 1 # 징검다리를 넘을 수 있는 최소 인원
right = max(stones) # 징검다리를 넘을 수 있는 최대 인원
while left <= right:
mid = (left + right) // 2 # 징검다리를 건너려는 사람의 수
cnt = 0 # mid보다 작거나 같은 값을 가진 연속적인 돌의 수를 확인
for stone in stones:
if stone - mid <= 0: # mid보다 작거나 같은 값을 가진 연속적인 돌의 수를 확인
cnt += 1
if cnt >= k: # 건너는 사람(mid)보다 작은 값을 가진 "연속적인" 돌의 수가 K개 이상이면 불가능
break
else:
cnt = 0 # "연속적인" 돌의 수를 확인하기 위해 mid보다 큰 값을 가진 돌이 중간에 껴있다면 cnt를 0으로 초기화
if cnt >= k: # 건너려는 사람의 수가 너무 많은거니까 줄이기
right = mid - 1
else:
left = mid + 1
return left