[고득점Kit][이분탐색] 징검다리 ⭐⭐⭐⭐
카테고리: Programmers
[이분탐색] 징검다리
난이도 ⭐⭐⭐⭐
💛 문제

💛 풀이
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int solution(int distance, vector<int> rocks, int n) {
int answer = 1;
int start = 1;
int end = distance;
sort(rocks.begin(), rocks.end());
while(start <= end)
{
int mid = (start + end) / 2;
int gap = 0;
int lastRock = 0;
int removeCount = 0;
for(int i = 0; i <= rocks.size(); i++)
{
if (i == rocks.size())
gap = distance - lastRock;
else
gap = rocks[i] - lastRock;
if (gap < mid)
removeCount++;
else if (i != rocks.size())
lastRock = rocks[i];
}
if (removeCount > n)
end = mid - 1;
else
{
start = mid + 1;
answer = mid;
}
}
return answer;
}
이분 탐색
- 고정 되어 정해져 있는 것은 무엇인지 (비교할 대상이 된다.)
n제거해야 하는 바위의 수
- 무엇을 이분 탐색으로 찾을 것인가 (
mid로 업뎃 해 나갈 것)- ‘각 지점 사이의 최소 간격’ 즉 바위간의 최소 간격.
- 의 최대값
- 찾으려고 하는 것이 속한 범위가 정렬이 되어 있는가
- ‘각 지점 사이의 최소 간격’의 범위의 최소값은 대충
1로 상정하고 최대값은 대충 바위가 하나도 없어서 출발지점 ~ 도착지점 간의 거리인distance로 정해놓는다. - 1 ~ distance 범위는 그 자체로 정렬이 되어 있다. 우리가 찾고자 하는 답인 ‘각 지점 사이의 최소 간격’는 이 정렬된 범위 내에 있으므로 이분 탐색을 사용하여 답을 찾을 수 있다.
int start = 1; int end = distance;
- ‘각 지점 사이의 최소 간격’의 범위의 최소값은 대충
- 찾고자 하는 것은
각 지점 사이의 최소 간격 - 분할 기준이 되는 비교는
제거해야할 바위 수와 현재의mid인각 지점 사이의 간격을 기준으로 했을 때 제거 한 바위의 수를 비교.
모든 경우의 수를 검사하지 않는다. 우리가 찾고자 하는 답인 ‘각 지점 사이의 최소 간격’을 업다운 형식으로 범위를 좁혀 나가 찾을 뿐이다. 업 다운의 기준은 정해져 있는 현재의 거리가
n개 이하로 바위를 제거하는 것이 가능한 거리인지를 따지는 것이 된다.n개 이하로 최대한 바위를 적게 제거해야 하는 이유는 거리의 최소값들 중에 가장 최대값이 되는 제거 케이스를 찾아야 하기 때문에 최대한 바위를 적게 제거 해야 한다.
- 문제에서 명확히 정해져 있는 것은
n제거해야 하는 바위의 수.- ⭐현재 정해놓은 '각 지점 사이의 최소 간격'인
mid를 기준으로 했을 때의 바위 제거 가능한 수를n과 비교하여 범위를 업데이트 및 좁혀 나간다. 👉 현재 정해둔 `mid`인 '각 지점 사이의 최소 간격' 보다 작은 간격의 바위는 제거하고 이 제거 횟수를 세자! 그리고 다 세고 난 후에n과 비교하자!for(int i = 0; i <= rocks.size(); i++) { if (i == rocks.size()) gap = distance - lastRock; else gap = rocks[i] - lastRock; if (gap < mid) removeCount++; else if (i != rocks.size()) lastRock = rocks[i]; }- 먼저
rocks를 정렬하여 바위의 위치들을 순서대로 정렬한다.sort(rocks.begin(), rocks.end()); rocks를 통해 위치별로 순서대로 바위 사이의 간격을 구한다. (바위1위치 - 출발지), (바위2위치 ~ 바위1위치), … , (마지막바위위치 - 마지막에서두번재바위위치), (도착지 - 마지막바위위치) 이렇게 총rocks.size() + 1개의 간격을 구해야 한다. for 문은rocks.size() + 1번 돌아야 함if (i == rocks.size())마지막 간격은 (도착지 - 마지막바위위치) 값인distance - lastRock.else그 외의 간격은 모두rocks[i] - lastRock
- 바위를 제거해나갈 것이기 때문에 간격을 구할 때 제거 된 바위의 그 앞 바위를 위치를 뺄셈 해주어야 한다. 따라서 이전 바위 위치를
lastRock변수에 저장한다. - 바위 제거하는 기준 👉
if (gap < mid) removeCount++; else if (i != rocks.size()) lastRock = rocks[i];- 위에서 구한 간격
gap이mid보다 작다면- 간격을 넓혀야 하므로 바위를 제거한다.
removeCount증가. for문이 끝나면 현재mid에서의 바위 제거 수가removeCount에 최종적으로 담기게 되고 후에 이걸n과 비교하여 이분 탐색 범위를 좁혀나갈 것이다. lastRock을 업데이트 하지 않는다. 현재 바위는 제거 되었으므로lastRock은 그대로 이전 바위다. 현재 바위가 3바위라면 이전 바위는 2바위이고 다음 바위는 4바위 가 될텐데 4바위는 2바위와의 간격을 구하도록lastRock을 업데이트 하지 않음.
- 간격을 넓혀야 하므로 바위를 제거한다.
- 위에서 구한 간격
gap이mid보다 크거나 작다면- 바위를 제거하지 않는다. 따라서
removeCount을 업데이트 하지 않는다. lastRock을 업데이트 한다. 현재 바위는 제거 되지 않았으므로 다음 바위에서 간격을 잴 이전 바위를 현재 바위로 업데이트 한다.- 단
lastRock을 현재 바위로 업데이트 하는 과정은 현재 순회 위치가 도착지가 아닐 때만 이루어져야 한다. 도착지일 땐i가rocks.size()값이니까 이를 고려하지 않으면last = rocks[i]에서 런타임 에러 남 !
- 단
- 바위를 제거하지 않는다. 따라서
- 위에서 구한 간격
- 먼저
- for문을 돌면서 구한 현재 정해놓은 ‘각 지점 사이의 최소 간격’인
mid를 기준으로 구한 바위 제거 수인removeCount를n과 비교하여 이분 탐색 범위를 업데이트 한다.removeCount > n: 현재 정해놓은mid기준에서n제거해야 하는 바위 보다 더 많이 제거 됐다면 현재 정해놓은 바위 간격인mid가 너무 크다는 것이다. 더 작은 범위로 좁혀야 한다.if (removeCount > n) end = mid - 1;removeCount <= n: 현재 정해놓은mid기준에서n제거해야 하는 바위 보다 같거나 더 적게 제거했다면n더 적게 제거 했다면 더 큰mid로 간격을 더 크게 해서 더 많이 제거할 수 있도록 해야 겠지만,n과 같다면 현재의mid가 충분하다는 것이므로 정답 후보가 될 수 있다. 더 큰 최대값이 있을 수 있으므로, 즉 더 큰mid에서도 정답이 있을 수 있으므로 더 큰 범위로 좁힌다.answer를 업데이트 한다. 현재의mid가 답이 될 수도 있으므로.. 이게 최대값이 될 수 있다.- 예를 들어 현재 간격을 5로 해두었을 때 제거한 바위의 수가
n과 같았다면 5 가 정답이 될 수 있다. 그러나 5보다 더 큰 수에서도 제거한 바위의 수가n과 같을 수 있기 때문에 일단answer에 5를 저장 후 더 큰 값의 간격에서도 바위를 제거해 봐야 한다!else { start = mid + 1; answer = mid; }
- 예를 들어 현재 간격을 5로 해두었을 때 제거한 바위의 수가
- ⭐현재 정해놓은 '각 지점 사이의 최소 간격'인
answer가 while문에서 한번도 업뎃되지 않을 경우를 대비하여 최소값인1를 초기값으로 잡는다.- 한번도 업뎃되지 않았다는건 1 ~ 59 범위의 모든 시간이
n명을 심사하기엔 전부 부족했다는 것이다.long long answer = end;
- 한번도 업뎃되지 않았다는건 1 ~ 59 범위의 모든 시간이
- 이 문제는 이분 탐색의 대표 코드에서
mid == answer가 되면return되어 빠져나오는 것과 다르게, 정확하게 딱 떨어지는 값을 찾는 것이 아닌n개를 제거 할 수 있는 최소 간격 중에서도 최대 간격을 찾는 것이기 때문에answer를 미리 업데이트 해둘 뿐, while을 탈출하는건start > end가 될 때 뿐이다.mid == answer가 되었다고해서 바로 while문을 빠져나와answer를 리턴하면 안된다.
- 간격의 ‘최소값’을
mid로서 정해두는 것이고 그mid를 기준으로 구한 바위 제거 수가n과 일치하는 것들 중 최대값이 되는mid를 구하는 것이 목표다.- 이 문제에서 최소는 정해 두는 것이고, 최대값은 구해야 하는 것
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기