본문 바로가기

코딩 테스트(Coding test)/Lv. 4

[프로그래머스/C++] 징검다리

미숙한 블로그 주인이 코딩테스트 문제를 풀어가는 과정을 담은 글입니다. 이 풀이가 효율적인 풀이가 아닐 수 있으며, 부정확한 정보가 많이 있을 수 있습니다. 보완해야할 점이 있다면 댓글로 남겨주세요!

 

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

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

징검다리

문제

출발지점부터 distance만큼 떨어진 곳에 도착지점이 있습니다. 그리고 그사이에는 바위들이 놓여있습니다. 바위 중 몇 개를 제거하려고 합니다.
예를 들어, 도착지점이 25만큼 떨어져 있고, 바위가 [2, 14, 11, 21, 17] 지점에 놓여있을 때 바위 2개를 제거하면 출발지점, 도착지점, 바위 간의 거리가 아래와 같습니다.

제거한 바위의 위치각 바위 사이의 거리거리의 최솟값
[21, 17] [2, 9, 3, 11] 2
[2, 21] [11, 3, 3, 8] 3
[2, 11] [14, 3, 4, 4] 3
[11, 21] [2, 12, 3, 8] 2
[2, 14] [11, 6, 4, 4] 4

위에서 구한 거리의 최솟값 중에 가장 큰 값은 4입니다.

출발지점부터 도착지점까지의 거리 distance, 바위들이 있는 위치를 담은 배열 rocks, 제거할 바위의 수 n이 매개변수로 주어질 때, 바위를 n개 제거한 뒤 각 지점 사이의 거리의 최솟값 중에 가장 큰 값을 return 하도록 solution 함수를 작성해주세요.

 

제한사항

  • 도착지점까지의 거리 distance는 1 이상 1,000,000,000 이하입니다.
  • 바위는 1개 이상 50,000개 이하가 있습니다.
  • n 은 1 이상 바위의 개수 이하입니다.

 

풀이

입출력 예시

 

풀이

바위를 n개 제거했을 때, 각 바위 사이의 거리들의 최솟값들 중 최댓값을 구해야한다. 그럼 n개의 조합으로 바위를 제거하면서 풀면 되겠지! 하고 쉽게 풀린다면 4단계 문제일리가 없다. 당장 distance의 최댓값이 10억이다.

 

그러니, 이런 큰 범위의 문제를 풀 때 자주 애용(?)했던 이분 탐색을 사용해야한다.

 

아래 풀이는 hyeon930님 블로그를 참고했습니다.

 

이분탐색으로 구하는 mid의 값이, 바위를 n개 이하 제거했을 때 각 바위 사이의 거리들의 최솟값이다.

그래서 다음 조건을 통해 모든 바위를 체크하면서 바위를 몇개 지우는지 확인한다.

// mid는 각 바위 사이의 거리들의 최솟값이다.
// 그런데 mid보다 작게 된다면 모순이므로 바위를 제거해준다.
if (현재 바위 - 이전 바위 < mid) {
    제거한 바위의 수++
}
else {
    이전 바위 = 현재 바위
}

위의 과정이 끝나고, 제거한 바위의 수가 n을 넘어버린다면, right의 값을 줄여준다.

반대의 경우라면 left의 값을 늘려준다.

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int solution(int distance, vector<int> rocks, int n) {
    int answer = 0;
    int start = 1;
    int end = distance;
    int r_size = rocks.size();
    
    // 이분탐색을 위해 정렬
    sort(rocks.begin(), rocks.end());
    // 마지막 지점을 추가
    rocks.push_back(distance);
    
    while (start <= end) {
        int count = 0; // 제거한 바위의 수
        int prev = 0; // 이전 바위의 위치
        int mid = (start + end) / 2; // 돌 사이 거리의 최솟값
        
        for (int rock : rocks) {
            // 현재 바위와 이전 바위 사이의 거리가 mid 보다 작으므로 현재 바위 제거
            if (rock - prev < mid) {
                count++;
            }
            // mid 보다 크거나 같으므로 지울 필요가 없다.
            else {
                prev = rock;
            }
        }
        
        // 제거한 바위의 수가 n보다 많으면, 최대 거리를 좁힌다.
        if (count > n) {
            end = mid - 1;
        }
        // 작거나 같으면 최솟값을 경신하고, 거리를 좁힌다.
        else {
            answer = max(mid, answer);
            start = mid + 1;
        }
    }
    
    return answer;
}