E - Knapsack 2

E - Knapsack 2

キーワード

解説

問題 (opens in a new tab)

i=0,1,,N, j=0,1,,Vi=0,1,\dots,N, ~ j=0,1,\dots,V に対して、dpi,jdp_{i,j} を次のように定義する。(ただし、 V=viV = \sum{v_i} )

ii 番目までの品物から価値の総和が jj 以上となるように選んだときの重さの最小値

dp0,j={0(j=0)(j>0)dp_{0,j} = \begin{cases} 0 & (j = 0) \\ \infty & (j > 0) \end{cases}

と初期化する。 ii 番目の品物まで使って良いとき、 ii 番目のしなものを使う場合と使わない場合の二通り考えられる。

  • 使わない場合は、 i1i-1 番目までの品物で、価値 jj を達成できるため、 i1i-1 番目まで使った時の最小重みと等しい。
  • 使う場合は、 i1i-1 番目までの品物で、価値 jvij-v_i を達成できる最小重みに wiw_i を加えたもの。

よって次のような漸化式が立てられる。

dpi,j={dpi1,j(j<vi)min{dpi1,j,dpi1,jvi+wi}(jvi)dp_{i,j} = \begin{cases} dp_{i-1,j} & (j < v_i) \\ \min\{dp_{i-1,j}, dp_{i-1,j-v_i} + w_i\} & (j \geq v_i) \end{cases}

最終的な答えは、 dpN,jWdp_{N,j} \leq W を満たす最大の jj となる。 dpN,jdp_{N,j}jj について単調増加であるため、二分探索で求めることもできる。

提出コード

int main() {
    int n, w; cin >> n >> w;
    vector<int> W(n), V(n); rep(i,0,n) cin >> W[i] >> V[i];
    vector<vector<ll>> dp(n+1, vector<ll>(MAX, 1e18));
    dp[0][0] = 0;
    rep(i,1,n+1) {
        rep(j,0,MAX) {
            dp[i][j] = dp[i-1][j];
            if(j-V[i-1] >= 0) dp[i][j] = min(dp[i][j], dp[i-1][j-V[i-1]] + W[i-1]);
        }
    }
    rrep(j,MAX-1,0) if(dp[n][j] <= w) {
        cout << j << endl;
        break;
    }
}