D - Knapsack 1

D - Knapsack 1

キーワード

解説

問題 (opens in a new tab)

i=0,1,,N, j=0,1,,Wi = 0,1,\dots,N, ~ j = 0,1,\dots,W に対して、dpi,jdp_{i,j} を次のように定義する。

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

dp0,j=0dp_{0,j} = 0 とする。

ii 番目まで使って良いとき、 ii 番目を使うか使わないかの二通りがある。

  • 使わない場合は、 i1i-1 番目までの品物で、重さの総和が jj 以下となるように選んだときの価値の最大値と同じ。
  • 使う場合は、 i1i-1 番目までの品物で、重さの総和が jwij - w_i 以下となるように選んだときの価値の最大値に、 ii 番目の価値を加えたもの。

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

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

jjwiw_i 未満のときは、 ii 番目の品物を選べないので、価値は変わらない。

最終的な答えは dpN,Wdp_{N,W} となる。

計算量は、一回の遷移が O(1)\mathrm{O}(1) で行えるため、全体で O(NW)\mathrm{O}(NW) となる。

提出コード

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>(w+1, 0));
    rep(i, 1, n+1) {
        rep(j, 1, w+1) {
            if (j - W[i-1] >= 0) dp[i][j] = max(dp[i-1][j], dp[i-1][j-W[i-1]] + V[i-1]);
            else dp[i][j] = dp[i-1][j];
        }
    }
    cout << dp[n][w] << endl;
}