20240329_금과 은 운반하기(프로그래머스)

2024. 3. 30. 03:20IT/TIL

오늘의 TIL은 금과 은 운반하기라는 프로그래머스의 문제에 대한 내용이다.

 

문제 링크

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

 

프로그래머스

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

programmers.co.kr

 

 

문제 설명

어느 왕국에 하나 이상의 도시들이 있다.

도시를 짓기 위해서는 도시를 짓는 장소에

금 a(kg)과 은 b(kg)이 전달되어야 한다.

 

각 도시에는 번호가 매겨져 있는데,

i번 도시에는 금 g[i](kg), 은 s[i](kg), 그리고 트럭 한 대가 있다.

i번 도시의 트럭은 오직 새 도시를 짓는 건설 장소와 i번 도시만을 왕복할 수 있으며

편도로 이동하는데 t[i] 시간이 걸리고, 최대 w[i](kg)의 광물을 운반할 수 있다.

모든 트럭은 같은 도로를 여러 번 왕복할 수 있으며, 연료는 무한대라고 가정한다.

 

정수 a, b와 정수 배열 g, s, w, t가 매개변수로 주어질 때,

주어진 정보를 바탕으로 각 도시의 트럭을 최적으로 운행했을 때,

새로운 도시를 건설하기 위해 금 a(kg)과 은 b(kg)을

전달할 수 있는 가장 빠른 시간을 구해 return 하시오.

 

 

제한 조건

0 <= a, b <= 10^9
1 <= g의 길이 = s의 길이 = w의 길이 = t의 길이 = 도시의 개수 <= 10^5
0 <= g[i], s[i] <= 10^9
1 <= w[i] <= 10^2
1 <= t[i] <= 10^5
a <= g의 모든 수의 합
b <= s의 모든 수의 합

 

 

해결 방안

문제에서 헷갈릴 수 있는 것이

지금 새로운 도시를 건설하는데 이 도시와 연결된 도시들을 배열로 주어주는 것이다.

즉, a, b를 만족하기 위해 최단 시간을 사용하는 방법을 찾는 문제인데

금과 은의 두 개의 변수를 가지고 있으며, 이를 동시에 운반하는 것도 가능한 점을 고려해야한다.

또 a와 b를 옮기는 과정에서 시간을 많이 사용하게 되는 방법이 있다면

그 시간동안 나머지를 다 옮기더라도 답은 가장 오래 걸리는 시간이 된다.

 

 

통과한 답안

더보기
using System;

public class Solution {
    public long solution(int a, int b, int[] g, int[] s, int[] w, int[] t) {
        
        long left = 0;
        long right = (long)1e15;
        long answer = right;

        while (left <= right)
        {
            long mid = (left + right) / 2;
            if (Check(mid, a, b, g, s, w, t))
            {
                answer = mid;
                right = mid - 1;
            }
            else
            {
                left = mid + 1;
            }
        }
        return answer;
    }
    private static bool Check(long time, int a, int b, int[] g, int[] s, int[] w, int[] t)
    {
        long totalGold = 0;
        long totalSilver = 0;
        long total = 0;

        for (int i = 0; i < g.Length; i++)
        {
            long trips = time / (t[i] * 2);
            if (time % (t[i] * 2) >= t[i]) trips++;
            long capacity = trips * w[i];

            long gold = Math.Min(capacity, g[i]);
            totalGold += gold;

            long silver = Math.Min(capacity, s[i]);
            totalSilver += silver;

            total += Math.Min(capacity, g[i] + s[i]);
        }

        return totalGold >= a && totalSilver >= b && total >= a + b;
    }
}

 

 

주어진 시간이 필요한 금과 은을 운반할 수 있는지 확인하는 Check 함수 구현

private static bool Check(long time, int a, int b, int[] g, int[] s, int[] w, int[] t)
{
    long totalGold = 0;
    long totalSilver = 0;
    long total = 0;

    for (int i = 0; i < g.Length; i++)
    {
        long trips = time / (t[i] * 2);
        if (time % (t[i] * 2) >= t[i]) trips++;
        long capacity = trips * w[i];

        long gold = Math.Min(capacity, g[i]);
        totalGold += gold;

        long silver = Math.Min(capacity, s[i]);
        totalSilver += silver;

        total += Math.Min(capacity, g[i] + s[i]);
    }

    return totalGold >= a && totalSilver >= b && total >= a + b;
}

 

Check 함수를 만들어서 지금 사용하는 시간이 

필요한 금과 은을 운반할 수 있는지 확인하는 함수를 구현한다.

각 도시별로 주어진 시간(time) 안에 트럭이 왕복할 수 있는 횟수(trips)를 계산하고,

만약 편도 추가가 가능하다면, 횟수를 1 증가시킨다.

각 도시의 트럭이 운반할 수 있는 최대 광물의 양(capacity)을 계산한다.

이 capacity를 사용해서 각 도시에서 운반할 수 있는 금과 은을 계산하고

운반 가능한 최대 양과 각 도시가 갖고 있는 양 중에서 작은 값을 택한다.

마지막으로 총 운반된 금과 은, 둘의 합을 계산하고

금이 a보다 크고, 은이 b보다 크고, 총량이 a + b 이상이라면 true를 반환한다.

 

이를 통해서 지금 주어진 시간(time)이 조건을 만족하는지 확인할 수 있다.

 

 

이진 탐색을 이용해서 조건을 충족하는 최소 시간 구하기

long left = 0;
long right = (long)1e15;
long answer = right;

while (left <= right)
{
    long mid = (left + right) / 2;
    if (Check(mid, a, b, g, s, w, t))
    {
        answer = mid;
        right = mid - 1;
    }
    else
    {
        left = mid + 1;
    }
}
return answer;

 

left(0)와 right(long에서의 최댓값)를 설정한다.

while 문을 이용하여 left와 right의 중간값(mid)을 설정하고

Check함수를 이용하여 mid의 시간이 조건을 충족하는지 확인한다.

 

만약 충족한다면 right를 mid - 1로 설정하여 탐색의 범위를 줄이고,

충족하지 않는다면 left를 mid + 1로 설정하여 탐색의 범위를 줄인다.

left가 right보다 큰 경우까지 반복하여 가장 적게 걸리는 시간을 answer로 저장한다.

 

 

오늘은 프로그래머스의 추천 문제로 나온 금과 은 운반하기라는 문제를 풀이해보았는데

처음에는 이진 탐색을 사용하지 않고 풀이하려고 하니 시간 초과가 나는 경우가 발생했다.

따라서 이진 탐색을 사용하게 되었는데, 이진 탐색은 매우 큰 범위의 문제라도

logN의 수준으로 빠르게 탐색할 수 있으므로 이를 사용하는 것이 타당하다고 깨달았다.

 

이진 탐색을 사용하는데 있어서 이를 판단할 Check 함수도 작성하였는데

모든 경우의 수들을 가지고 이 경우에 필요한 조건을 만족하는 함수를 작성하는 과정에서

금과 은의 양이 각 도시가 갖고 있는 양으로 충분한지 확인하기 위해

도시에 있는 금과 은의 양의 합과 필요한 금과 은의 양의 합을 비교하는 과정을 추가함으로써

판단하며 생겼던 문제를 해결할 수 있었다.

 

오늘은 알고리즘 문제를 풀면서 이진 탐색에 대해 조금 더 공부할 수 있는 시간을 가졌다고 생각한다.

 

'IT > TIL' 카테고리의 다른 글

20240511_택시 기하학  (0) 2024.05.11
20240402_StringBuilder  (0) 2024.04.02
20240328_A로 B 만들기(프로그래머스)  (0) 2024.03.28
20240328_DFS(타겟 넘버)  (0) 2024.03.28
20240326_HashSet, ref  (0) 2024.03.26