문제의 제한사항의 일부를 보면 다음과 같다.
stones
배열의 크기는 1 이상 200,000 이하입니다.stones
배열 각 원소들의 값은 1 이상 200,000,000 이하인 자연수입니다.위의 조건 때문에 은 당연히 불가능하다.
우리가 원하는 것은 건널 수 있는 최대 사람의 수이다. 여기서 이진탐색을 떠올렸다.
기본 아이디어는, 사람 수를 기준(mid
)으로 설정하고 stones
배열의 각 원소에서 mid
만큼 뺀 후 연속되는 0의 최대 길이를 찾는 방식이다.
def solution(stones, k):
def helper(stones):
n = len(stones)
_max = 0
start = -1
for i in range(n):
if stones[i] == 0:
if start == -1:
start = i
else:
if start != -1:
_max = max(_max, i - start + 1)
start = -1
continue
return _max
answer = 0
l, r = 1, max(stones)
while l <= r:
mid = l + (r - l) // 2
new_stones = [stone - mid if stone >= mid else 0 for stone in stones]
_max = helper(new_stones)
if _max < k:
l = mid + 1
elif _max >= k:
answer = _max
r = mid - 1
return answer
알고리즘은 크게 2가지이다.
처음에 어떻게 0의 길이를 찾을 것인가 고민을 하다가, 포인터(start)를 하나 사용해서 현재 탐색중인 원소의 값이 0이면 해당 원소의 위치로 포인터를 갱신한다.
그리고 만약, 0이 아닌 원소를 마주친다면 현재 원소의 위치에서 포인터까지의 길이를 구해 저장해 _max(연속된 0의 길이의 최댓값)
과 비교하여 저장한다.
문제의 예시 입력은 성공하지만, 다른 테스트 케이스는 모두 틀리는 것을보아 이 알고리즘이 문제인 것 같다.
def solution(stones, k):
# 연속적인 0의 길이(개수) 찾는 함수
def find_consecutive_zero_length(stones, mid):
zero_cnt = 0 # 0의 길이
for stone in stones:
if stone - mid <= 0:
zero_cnt += 1
else:
zero_cnt = 0
# 주어진 k보다 크거나 같다면
# 즉, 건널 수 있는 칸의 수보다 같거나 많게 건너 뛰었음을 의미.
if zero_cnt >= k:
break
return zero_cnt
# 이진 탐색 포인터
# 기준 : 다리를 건널 수 있는 최대 사람 수
l, r = 1, max(stones)
answer = 0
while l <= r:
mid = (l + r) // 2
zero_cnt = find_consecutive_zero_length(stones, mid) # 연속적인 0의 길이
if zero_cnt >= k: # 너무 많이 or 딱 맞게 칸을 건너 뜀
answer = mid
r = mid - 1 # 왼쪽 부분 탐색
else: # 너무 적게 칸을 건너 뜀
l = mid + 1 # 오른쪽 부분 탐색
return answer
연속된 0의 길이를 찾는 알고리즘을 수정했다.
stones(배열)
의 원소에서 mid(설정한 건널 수 있는 사람 수)
를 빼고 0보다 작거나 같다면 연속된 0의 개수를 갱신한다.(건널수 없는 돌다리이므로 건너뛰어야한다.)
만약, 0보다 크다면 연속된 0이 아니므로 연속된 0의 개수를 0으로 설정한다.
만약, 연속된 0의 개수가 인자로 주어진 k(한 번에 건너뛸 수 있는 디딤돌의 최대 칸수)
보다 크거나 같다면 연속된 0의 개수를 반환한다.
이제 반환한 연속된 0의 개수(zero_cnt)를 토대로 이진탐색의 다음 과정을 수행하면 된다.
zero_cnt
가 k
보다 크거나 같다면 왼쪽 부분 탐색, 그 반대라면 오른쪽 부분 탐색을 수행한다.