코딩테스트/BAEKJOON

백준: 1106. 호텔 (C++)

Sfer7 2025. 3. 7. 13:01
1. 문제 정보

 

2. 문제 풀이

 

전형적인 배낭 문제 유형이에요 !
하지만 이번 문제를 풀면서 풀이 과정을 개선한 점, 그리고 새롭게 배운 점이 있어 정리해볼까 해요 🥰

 

  • 2차원 배열 풀이
    원래 저는 배낭 문제를 풀 때 2차원 배열 형태로 풀어왔었어요
    첫 번째 예시를 예로 들어봅시다

    먼저 DP 배열은 DP[1001][100001]로 작성해줬어요
    N이 1000보다 작거나 같고, 각 도시에서 낼 수 있는 비용이 최대 100이므로
    C = 1000일 때 필요한 최대 비용은 100 * 1000이 됩니다

    이에 맞춰 예제 입력 1번에 대한 풀이를 해볼게요


    초기에는 이와 같은 2차원  배열로 시작합니다
    1번 도시의 경우, 3의 홍보 비용으로 5의 고객을 유치할 수 있어요
    이를 테이블에 반영하게 되면


    이러한 상태가 됩니다
    우리가 구하고 싶은 값은 최소 12명을 유치하는 최솟값이기 때문에, 현재까지 최소 비용은 9가 되겠네요 😊

    이제 2번 도시도 반영해보도록 하겠습니다


    일단 비용이 3일 때까지만 반영해보도록 합시다
    이때 2번 도시에서 비용 3으로 얻을 수 있는 고객 수는 3이 되겠죠?
    하지만 우리가 구하고 싶은 것은 비용으로 얻을 수 있는 최대 고객 수잖아요?
    때문에 이전까지 도시에서 얻을 수 있는 최대 고객 수현재 도시에서 얻을 수 있는 최대 고객 수를 비교해줍니다
    이에 맞춰서 5를 반영하게 된 것이에요 😀
    점화식으로 보자면 DP[i][j] = DP[i - 1][j]가 된 것이지요

    계속 이어나가 보겠습니다


    이번엔 비용을 4 투자했을 때 상황입니다
    2번 도시만 반영했을 때는 4, 1번 도시만 반영했을 때는 5가 되겠죠?
    하지만 이 때 1번 도시와 2번 도시를 동시에 반영하고 싶다면 어떻게 해야 할까요?

    현재까지 사용한 비용에서 현재 도시 비용의 가격을 제했을 때 가치을 참고하면 됩니다
    점화식으로 보자면 DP[i][j] = DP[i][j - cost[i]] + customer[i]가 되겠죠
    현재 상태를 기준으로 보자면 DP[2][4] = DP[2][4 - 1] + 1 = DP[2][3] + 1 = 5 + 1 = 6이 되는거지요 👀
    이 두 가지 경우를 합치게 되면, 점화식은 이와 같이 되게 됩니다
    DP[i][j] = max(DP[i - 1][j], DP[i][j - cost[i]] + customer[i])
    이때 배열이 음수가 되면 안되므로, j >= cost[i]일 때만 가능하도록 해야겠죠 😊

    이와 같은 방식으로 배열을 계속 채워나갑니다


    채워 나가다 보면, 우리가 구하고자 하는 최소 인원 수에 딱 맞는 비용이 나타나네요 !
    즉, 답은 8이 됩니다 🥰

     
    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    #define INF (int)2e9
    
    int ans = INF;
    int c, n;
    int dp[21][1000001];
    vector<pair<int, int>> v;
    
    int main() {
        ios_base::sync_with_stdio(0);
        cin.tie(0);
    
        cin >> c >> n;
        v.resize(n + 1);
    
        for (int i = 1; i <= n; i++) {
            cin >> v[i].first >> v[i].second;
        }
    
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= 1000000; j++) {
                if (j >= v[i].first) {
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - v[i].first] + v[i].second);
                }
                else {
                    dp[i][j] = dp[i - 1][j];
                }
    
                if (dp[i][j] >= c) {
                    ans = min(ans, j);
                    break;
                }
            }
        }
    
        cout << ans;
    }
     


    통과는 했지만, 메모리 사용량이 상당히 높네요 . . !
    풀이를 봤을 때 더 간단히 1차원 배열로도 가능해 보이지 않나요?
    이제 이 부분에 대해 정리해볼게요 !

  • 1차원 배열 풀이
    이 문제는 사실 1차원 배열로도 풀 수 있어요 😊
    이 부분에 대해 살짝 정리를 해보자면

    특수한 조건이 없을 때, 0-1 배낭 문제무한 배낭 문제의 경우 1차원 배열로 풀이가 가능합니다

    이번 문제는 단순히 목표 인원수만 계산하는 무한 배낭 문제이기 때문에 역시 1차원 배열로 풀이가 가능합니다
    풀이는 사실상 거의 동일한데, 배열에서 몇 번 도시냐를 나타내는 차원은 필요가 없을 것 같아요 👀
    때문에 DP[100001]로 형태를 변경해줍니다
    그리고 하던대로 배열의 앞에서부터 순차적으로 적용해주면 1차원 배열로만 갱신이 가능합니다

     
    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    #define INF (int)2e9
    
    int ans = INF;
    int c, n;
    int dp[1000001];
    vector<pair<int, int>> v;
    
    int main() {
        ios_base::sync_with_stdio(0);
        cin.tie(0);
    
        cin >> c >> n;
        v.resize(n + 1);
    
        for (int i = 1; i <= n; i++) {
            cin >> v[i].first >> v[i].second;
        }
    
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= 1000000; j++) {
                if (j >= v[i].first) {
                    dp[j] = max(dp[j], dp[j - v[i].first] + v[i].second);
    
                    if (dp[j] >= c) {
                        ans = min(ans, j);
                        break;
                    }
                }
            }
        }
    
        cout << ans;
    }
     


    어떤가요? 코드도 훨씬 짧아졌고, 메모리도 크게 줄일 수 있게 되었죠?
    이런 방식으로도 풀 수 있답니다 😊

    이때 0-1 배낭무한 배낭의 1차원 풀이의 차이점은 반영 순서에 있는데요 !
    무한 배낭의 경우 앞에서부터 차례대로 갱신하게 되면 이와 같이 갯수가 몇 개든 자동적으로 반영이 되죠?
    하지만 0-1 배낭의 경우 이렇게 적용하면 안되므로, 배낭 반영을 역순으로 해야해요
    이렇게 하면 하나의 물건에 대해서만 반영할 수 있답니다 🥰