86053. 금과 은 운반하기
프로그래머스 86053번 풀이
이 글은 Obsidian에서 마이그레이션되었으며, 그 과정에서 AI의 도움을 받았습니다. 오류나 누락된 내용이 있다면 댓글로 알려주세요!
문제
코딩테스트 연습 - 금과 은 운반하기 | 프로그래머스 스쿨
풀이
아이디어
이 문제는 이분탐색으로 풀어야 한다!
이 문제는 답을 구하는 문제가 아닌 답인지 아닌지를 확인하는 문제이다. 무슨말이냐면, 어떤 경로를 거쳐야 최단 시간 안에 운송할 수 있는지를 구하는게 아닌, 어떤 시간 안에 어떻게든 운송이 가능한지만을 확인하는 문제라는 것이다.
그리고 단조성을 띈다. 단조성은 어떤 시점을 기준으로 가능/불가능, 정답/오답이 갈리는 특징을 말한다. 이 문제 역시 아래와 같은 특징을 가지므로 단조성을 갖고 있다.
- 어떤 시간 Time에서 운송이 가능하면 그보다 더 큰 시간들에서는 (가능한 트럭의 왕복 횟수가 증가하기 때문에) 무조건 시간 안에 운송이 가능하다.
- 어떤 시간 Time에서 운송이 불가능하면, 그보다 더 작은 시간들에서도 (가능한 트럭의 왕복 횟수가 감소하기 때문에) 시간 안에 운송이 불가능하다.
따라서 이 문제는 이분탐색으로 풀어야 한다!
운반 가능 여부 확인하는 check 함수 구현하기
check(time) 함수는 time 시간이 주어졌을 때 운반이 가능한지를 확인하는 함수이다.
check 함수에서 확인해야 하는 조건은 아래 3가지 조건이다.
- 모든 도시에서 time 시간 안에 실을 수 있는 금의 총 합 >= a (필요한 금의 양)
- 모든 도시에서 time 시간 안에 실을 수 있는 은의 총 합 >= b (필요한 은의 양)
- 모든 도시에서 time 시간 안에 실을 수 있는 광물(금과 은)의 총 합 >= a + b
1번, 2번 조건은 금이나 은 자체가 부족한 경우를 막는 조건이다. time 시간 안에 트럭은 충분히 운송할 수 있더라도 전체 도시들에 있는 총량이 필요한 양보다 작으면 불가능하기 때문에 이 부분을 체크하기 위한 조건이다.
마지막 3번 조건은 트럭이 두 광물을 동시에 실어야 하기 때문에 확인해야 하는 조건이다. 1번, 2번 조건을 확인할 때에는 같은 트럭 용량을 두 조건이 각각 최대로 사용하고 있기 때문에, 동시에 싣는 것이 불가능할수도 있다. 따라서 동시에 가능한지도 함께 확인하는 것이다.
그래서 check(time)의 구조는 모든 도시를 돌면서 아래 3가지를 구해서 확인하는 식으로 되어 있다.
- 그 도시가 time 시간 안에 새 도시에 보낼 수 있는 최대 금
- 그 도시가 time 시간 안에 새 도시에 보낼 수 있는 최대 은
- 그 도시가 time 시간 안에 새 도시에 보낼 수 있는 최대 광물
이것을 구하려면 time 시간 안에 트럭이 몇번 새 도시에 도착할 수 있는지를 구해야 하는데, 중요한 것은 운반이 “도착” 시점에 이뤄진다는 것이다. 그래서 기본적으로는 왕복 시간을 이용해서 횟수를 구하되, 딱 한번 새 도시로 편도로 갈 수 있는 여유 시간이 남는다면 한번 더 운송하도록 해야 한다. 코드로 구현하면 아래와 같다.
const maxDeliveries = Math.floor(time / (2 * t[i])) + (time % (2 * t[i]) >= t[i] ? 1 : 0);이분탐색 구현하기!
check 함수까지 구현했으니 이제 이분탐색만 하면 된다. 이건 간단하니까 그냥 코드만 남겨둔다.
// 이분탐색
let left = 0, right = 10 ** 15;
while (left < right) {
const mid = Math.floor((right + left) / 2);
if (check(mid)) right = mid;
else left = mid + 1;
}코드
function solution(a, b, g, s, w, t) {
// time 시간 안에 모든 도시에서 필요한 광물들을 운반할 수 있는지를 확인
function check(time) {
const cityCount = g.length;
let gold = 0, silver = 0, total = 0;
for (let i = 0; i < cityCount; i++) {
// time 시간 안에 "운반"할 수 있는 최대 횟수
const maxDeliveries = Math.floor(time / (2 * t[i])) + (time % (2 * t[i]) >= t[i] ? 1 : 0);
// time 시간 안에 운반할 수 있는 광물의 최대 양
const cap = maxDeliveries * w[i];
gold += Math.min(g[i], cap);
silver += Math.min(s[i], cap);
total += Math.min(g[i] + s[i], cap);
}
return gold >= a && silver >= b && total >= a + b;
}
// 이분탐색
let left = 0, right = 10 ** 15;
while (left < right) {
const mid = Math.floor((right + left) / 2);
if (check(mid)) right = mid;
else left = mid + 1;
}
return left;
}이진탐색 문제 재밌다… 취향에 맞음