문제 설명
[본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.]
카카오 초등학교의 "니니즈 친구들"이 "라이언" 선생님과 함께 가을 소풍을 가는 중에 징검다리가 있는 개울을 만나서 건너편으로 건너려고 합니다. "라이언" 선생님은 "니니즈 친구들"이 무사히 징검다리를 건널 수 있도록 다음과 같이 규칙을 만들었습니다.
"니니즈 친구들"은 개울의 왼쪽에 있으며, 개울의 오른쪽 건너편에 도착해야 징검다리를 건넌 것으로 인정합니다.
"니니즈 친구들"은 한 번에 한 명씩 징검다리를 건너야 하며, 한 친구가 징검다리를 모두 건넌 후에 그 다음 친구가 건너기 시작합니다.
디딤돌에 적힌 숫자가 순서대로 담긴 배열 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