[Programmers] LV.3 징검다리 건너기

정재욱·2023년 5월 24일
0

Algorithm

목록 보기
30/33

[2019 카카오 개발자 겨울 인턴십] 프로그래머스 LV.3 징검다리 건너기

문제

문제 설명
[본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.]

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

  • 징검다리는 일렬로 놓여 있고 각 징검다리의 디딤돌에는 모두 숫자가 적혀 있으며 디딤돌의 숫자는 한 번 밟을 때마다 1씩 줄어듭니다.
  • 디딤돌의 숫자가 0이 되면 더 이상 밟을 수 없으며 이때는 그 다음 디딤돌로 한번에 여러 칸을 건너 뛸 수 있습니다.
  • 단, 다음으로 밟을 수 있는 디딤돌이 여러 개인 경우 무조건 가장 가까운 디딤돌로만 건너뛸 수 있습니다.

"니니즈 친구들"은 개울의 왼쪽에 있으며, 개울의 오른쪽 건너편에 도착해야 징검다리를 건넌 것으로 인정합니다.
"니니즈 친구들"은 한 번에 한 명씩 징검다리를 건너야 하며, 한 친구가 징검다리를 모두 건넌 후에 그 다음 친구가 건너기 시작합니다.

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

[제한사항]

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

풀이

처음에는 슬라이딩 윈도우를 사용하여 풀고자 했다.
정확도에서는 다 맞았지만, 효율성에서 1개를 빼고 다 시간초과가 났다....

그래서 질문하기에 들어가서 정답을 보진 않고, 문제 힌트만 봤는데, 이진탐색을 사용했다는 제목을 봤다.

이후 바로 다시 문제를 보며 이진탐색으로 어떻게 문제를 풀어볼까 고민하며 문제를 풀기 시작했다.

  1. 우선, 가장 중요한 left, right, mid징검다리를 건너려는 사람의 수로 정했다.

  2. 다음으로 연속적인 돌의 수가 K보다 작은지는 stones배열의 값과 mid를 이용하여 구할 수 있었다.

    • 반복문을 사용해서 stones 배열의 값과 mid의 차이가 0이하라면(stones[i] - mid <= 0),
      건너려는 사람의 수보다 돌의 값이 작거나 같아 0이 된다는 의미이고, 우리는 이런 돌이 연속으로 k개 미만이 되는 사람의 수(mid)를 구해야한다.

    • 이를 위해 stones[i] - mid <= 0이 되는 stones 배열의 값이 몇개가 있는지 cnt에 저장한다.

    • "연속적인 돌의 개수"를 세야하므로, 만약 stones 배열의 값을 하나씩 조사하다가, mid보다 큰 값이 나온다면, 연속이 깨지게 되므로 cnt를 0으로 초기화 해준다.

    • 이러한 cntK보다 크거나 같을때는 mid명의 사람이 건너기 불가능하므로 이진탐색의 탐색 범위를 줄인다.

  3. 이후 건너려는 사람의 수보다 작은 값을 가진 연속되는 돌의 개수가 k 이상이면
    즉, cnt >= k이면 건너려는 사람의 수가 너무 많은거니까 right = mid - 1을 통해 범위를 조절.

  4. 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
profile
AI 서비스 엔지니어를 목표로 공부하고 있습니다.

0개의 댓글