징검다리 건너기 (level 3)

원용현·2022년 9월 30일
0

프로그래머스

목록 보기
26/49

링크

https://school.programmers.co.kr/learn/courses/30/lessons/64062

문제

카카오 초등학교의 "니니즈 친구들"이 "라이언" 선생님과 함께 가을 소풍을 가는 중에 징검다리가 있는 개울을 만나서 건너편으로 건너려고 합니다. "라이언" 선생님은 "니니즈 친구들"이 무사히 징검다리를 건널 수 있도록 다음과 같이 규칙을 만들었습니다.

징검다리는 일렬로 놓여 있고 각 징검다리의 디딤돌에는 모두 숫자가 적혀 있으며 디딤돌의 숫자는 한 번 밟을 때마다 1씩 줄어듭니다.
디딤돌의 숫자가 0이 되면 더 이상 밟을 수 없으며 이때는 그 다음 디딤돌로 한번에 여러 칸을 건너 뛸 수 있습니다.
단, 다음으로 밟을 수 있는 디딤돌이 여러 개인 경우 무조건 가장 가까운 디딤돌로만 건너뛸 수 있습니다.
"니니즈 친구들"은 개울의 왼쪽에 있으며, 개울의 오른쪽 건너편에 도착해야 징검다리를 건넌 것으로 인정합니다.
"니니즈 친구들"은 한 번에 한 명씩 징검다리를 건너야 하며, 한 친구가 징검다리를 모두 건넌 후에 그 다음 친구가 건너기 시작합니다.

디딤돌에 적힌 숫자가 순서대로 담긴 배열 stones와 한 번에 건너뛸 수 있는 디딤돌의 최대 칸수 k가 매개변수로 주어질 때, 최대 몇 명까지 징검다리를 건널 수 있는지 return 하도록 solution 함수를 완성해주세요.

[제한사항]
1. 징검다리를 건너야 하는 니니즈 친구들의 수는 무제한 이라고 간주합니다.
2. stones 배열의 크기는 1 이상 200,000 이하입니다.
3. stones 배열 각 원소들의 값은 1 이상 200,000,000 이하인 자연수입니다.
4. k는 1 이상 stones의 길이 이하인 자연수입니다.

문제 이해

문제만 보면 stones 배열을 한명이 지나갈 때 -1 씩 진행하며 카운트를 세고 0의 길이가 k 만큼 나오는 순간을 확인하는 문제처럼 보인다. 하지만 해당 로직으로 코드를 작성하여 확인을 하면 효율성에서 모두 실패를 하는 결과를 볼 수 있다.

for문 자체도 O(n)의 시간복잡도를 가지는데 k의 값이 크게 나올 경우에는 이것 역시 O(n)의 시간복잡도를 가져 결국 O(n^2)과 같은 시간복잡도를 가져 매우 오래 걸리기 때문이다.

따라서 이 문제는 시간복잡도를 최대한 낮춰서 문제를 해결해야한다.

여기서 사용하는 알로리즘 로직은 이분탐색기법이다. k값으로 나올 수 있는 최소, 최대값을 가지고 이분탐색기법을 적용하여 문제의 답을 구한다.

k의 최대값, 최소값을 left, right로 생각하고 이분탐색을 적용한다. 중간값을 구해 중간값 만큼의 사람이 지나갔다고 생각하여 계산하는데 계산 결과에 따라서 left, right를 옮겨가며 답을 구한다.

코드

function solution(stones, k) {
    let left = 1
    let right = 200000000
    let zeroCount = 0
    let mid = 0
    
    while(left <= right) {
        mid = Math.floor((left + right) / 2)
        zeroCount = 0
        
        for(let i = 0; i < stones.length; i++) {
            let val = stones[i] - mid
            val <= 0 ? zeroCount++ : zeroCount = 0
            
            if(zeroCount === k) {
                break
            }
        }
        
        zeroCount === k ? right = mid - 1 : left = mid + 1
    }
    return left
}

코드 풀이

반복을 진행하면 left와 right는 점점 가까워지므로 right가 더 큰 경우에 반복을 진행한다.

stones 배열 요소 하나 씩 뽑아 중간값만큼을 빼며 뺀 결과가 0 이하이면 zeroCount를 +1 해주고 아니라면 zeroCount를 초기화 해준다.

계산을 반복하며 zeroCount가 k가 된다면 바로 for문 반복을 끝내고 right의 값을 조절하고, 아니라면 left의 값을 조절해나간다.

해당 과정이 while문이 끝날 때 까지 반복된 후의 left 값이 주어진 문제의 정답이 된다.

0개의 댓글