카테고리 없음

[프로그래머스] 징검다리 건너기 - Java

BGK97 2025. 3. 25. 19:46

문제 출처

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

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

문제 설명

 

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

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

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

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

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

[제한사항]

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

[입출력 예]

 

입출력 예에 대한 설명


입출력 예 #1

첫 번째 친구는 다음과 같이 징검다리를 건널 수 있습니다.

첫 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다.
두 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다.

두 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다.
세 번째 친구도 아래 그림과 같이 징검다리를 건널 수 있습니다.

세 번째 친구가 징검다리를 건넌 후 디딤돌에 적힌 숫자는 아래 그림과 같습니다.
네 번째 친구가 징검다리를 건너려면, 세 번째 디딤돌에서 일곱 번째 디딤돌로 네 칸을 건너뛰어야 합니다. 하지만 k = 3 이므로 건너뛸 수 없습니다.

따라서 최대 3명이 디딤돌을 모두 건널 수 있습니다.

풀이 방법

  • 문제에서 읽으면 알겠지만, n명이 지나갔을 때, 각 돌은 value - n 만큼 값이 깎이며 어느샌가 0인 돌들이 많이 생길 것이다. 이때 k개만큼 돌만큼의 간격이 생기면 못 지나가는 것!
    • 그러나 n를 1부터 탐색하기엔 최대 200,000 크기의 배열을 계속 순차적으로 탐색하면 무조건 시간초과가 날것
  • 따라서, 우리는 돌들의 최소와 최대를 구해서 파라메트릭 서치를 실시하면 된다.

파라메트릭 서치를 하는 이유?

  • 완전탐색의 경우, 배열의 크기가 크면 클수록 n의 제곱 배수처럼 기하급수적으로 커짐
  • 파라메트릭서치의 경우, 배열의 크기가 크더라도 log 배수로 증가함. 

 

  • 완전탐색으로는 약 8번의 탐색 후에 8을 찾을 수 있다.

  • 이분 탐색의 경우, 약 3번의 연산후에 바로 8을 찾아낼 수 있다.
물론 초반 값을 찾는다면 (2~3정도...) 완전탐색이 유리한 부분도 있겠지만, 수가 커지면 커질수록 이분탐색의 연산수가 급격하게 줄어들게 된다. 
-> 예시로, 1~1000사이에서 975를 찾는 연산은 완전탐색은 975회, 이분탐색은 약 8회 정도의 연산만 수행이 된다.

풀이 코드

class Solution {
    public int solution(int[] stones, int k) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        
        //순회하면서 최소, 최대 찾기
        for(int stone: stones){
            min = Math.min(min, stone);
            max = Math.max(max, stone);
        }
        
        //이분..탐색?
        while(min < max){
            //중간 값
            int mid = (min + max + 1) / 2;
            
            if(isPossible(stones, mid, k)){
                min = mid;
            }
            else {
                max = mid - 1;
            }
        }
        
        return max;
    }
    
    //해당 값의 돌로도 이동이 가능한지 테스트
    public boolean isPossible(int[] stones, int mid, int k){
        int count = 0;
        
        for(int stone: stones){
            if(stone - mid < 0){
                count++;
            }           
            else {
                count = 0;
            }
            
            //카운트가 넘어가면 못건너가므로...
            if(count >= k){
                return false;
            }
        }
        return true;
    }
}

결과

회고

  • 뭘 써야할지 유추하는 게 제일 어려운듯한