SUSHI (중, p. 355)

문제#

image.png


어떻게 풀었나?#

슬라이딩 윈도우 기법을 적용하여 풀었다.

m이 21억정도 되는데, price로 들어오는 값이 100의 배수이므로 일단 100으로 나누고 시작했다. 그러면 2100만정도 되는 값이고, 배열에 담을 수 있는 적당한 개수만 dp배열로 만들어서 상태공간을 구성했다. 최대 가격이 2만인데, 100으로 나누면 200이므로, 상태공간은 dp[2][201] 이상만 되면 된다.


정답 코드#

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int price[21], like[21];
int dp[2][20001];
int n;
const int GIJUN = 20000;
int main() { 
    ios::sync_with_stdio(false); cin.tie(0);
    int tc; cin >> tc;
    while(tc--) {
        ll m;
        cin >> n >> m;
        m/=100;
        memset(dp,0,sizeof(dp));
        for(int i=0; i<n; i++) {
            cin >> price[i] >> like[i];
            price[i]/=100;
        }
        int i,t=0,j=0;
        for(i=0; i<=m; i++,j++) {
            for(int k=0; k<n; k++) {
                if(j-price[k]>=0) {
                    dp[t][j] = max(dp[t][j], dp[t][j - price[k]] + like[k]);
                }
            }
            if(j==GIJUN-1) break;
        }
        if(j==GIJUN-1) {
            i++;
            t ^= 1;
            j=0;
        }
        for(; i<=m; i++,j++) {
            for(int k=0; k<n; k++) {
                if(j-price[k]>=0) {
                    dp[t][j] = max(dp[t][j], dp[t][j - price[k]] + like[k]);
                }
                else {
                    dp[t][j] = max(dp[t][j], dp[t^1][GIJUN+(j-price[k])] + like[k]);
                }
            }
            if(j==GIJUN-1) {
                t^=1;
                j=-1;
                //memset(dp[t],0,sizeof(dp[t]));
            }
        }

        if(j-1>=0) printf("%d\n", dp[t][j-1]);
        else printf("%d\n", dp[t^1][GIJUN-1]);

    }
    return 0; 
}

책을 본 이후의 수정 코드#

상태공간을 복잡하게 dp[2][201]로 나타낼 필요 없이, 나머지 연산으로 dp[201] 상태공간만 유지하면 간단하게 해결되는 것을 알게 됨. %연산은 시간이 좀 소요돼서 bit연산으로 수정했고, 상태공간도 201에 가까운 256으로 잡지 않고(%연산을 bit연산으로 수정했기 떄문에 2의 배수로 잡아야 했음.), 1<<16 정도로 대충 크게 잡음.

#include <bits/stdc++.h>
using namespace std;
int price[21], like[21];
int n, m;
const int MASK = (1 << 16) - 1;
int dp[MASK + 1];
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int tc; cin >> tc;
    while(tc--) {
        cin >> n >> m;
        m /= 100;
        memset(dp, 0, sizeof(dp));
        for(int i = 0; i < n; i++) {
            cin >> price[i] >> like[i];
            price[i] /= 100;
        }
        for(int i = 0; i <= m; i++) {
            int& result = dp[i & MASK];
            for(int j = 0; j < n; j++) {
                if(i - price[j] >= 0) {
                    result = max(result, dp[(i - price[j]) & MASK] + like[j]);
                }
            }
        }

        printf("%d\n", dp[m & MASK]);
    }
    return 0;
}