Top
yukicoder No.617「Nafmo、買い出しに行く」
$n$ 個の商品にはそれぞれ重さがあります。
総重量が $K$ 以下で最大になるように商品を選んだとき、その総重量を求めてください。
・$1 \le n \le 20$
・$0 \le K \le 2 \times 10^{6}$
・$1 \le$ 各商品の重さ $\le 10^{5}$

dp[i番目を選ぶか選ばないか悩んでいる][i番目について悩む前までの総重量] := 可否
    
という動的計画法が解き方のひとつとして考えられる。

#include <iostream>
#include <algorithm>
using namespace std;

int main(void) {
  int n, K; scanf("%d%d", &n, &K);
  vector<int> a(n);
  for(int i=0; i<n; ++i) { scanf("%d", &a[i]); }
  // dp[i番目を選ぶか選ばないか悩んでいる][これまでの重さ] := 可否
  vector<vector<bool>> dp(n+1, vector<bool>(K+1));
  dp[0][0] = true;
  for(int i=0; i<n; ++i) {
    for(int k=0; k<=K; ++k) { // K を含めるのを忘れないように
      if(!dp[i][k]) { continue; }
      dp[i+1][k]        = true; // i番目を選ばない
      if(k + a[i] <= K) {
        dp[i+1][k+a[i]] = true; // i番目を選ぶ
      }
    }
  }
  for(int k=K; k>=0; --k) {
    if(dp[n][k]) {
      printf("%d\n", k);
      return 0;
    }
  }
  throw;
}
    

今回の問題では必要ないが、二つの一次元配列を交互に使うことでメモリの節約をすると以下のようになる。
#include <iostream>
#include <algorithm>
using namespace std;

int main(void) {
  int n, K; scanf("%d%d", &n, &K);
  vector<int> a(n);
  for(int i=0; i<n; ++i) { scanf("%d", &a[i]); }
  // dp[i番目を選ぶか選ばないか悩んでいる状態でのこれまでの重さ] := 可否
  vector<bool> dp(K+1);
  dp[0] = true;
  for(int i=0; i<n; ++i) {
    vector<bool> ndp(K+1);
    for(int k=0; k<=K; ++k) { // K を含めるのを忘れないように
      if(!dp[k]) { continue; }
      ndp[k]        = true; // i番目を選ばない
      if(k + a[i] <= K) {
        ndp[k+a[i]] = true; // i番目を選ぶ
      }
    }
    dp = ndp;
  }
  for(int k=K; k>=0; --k) {
    if(dp[k]) {
      printf("%d\n", k);
      return 0;
    }
  }
  throw;
}
    

(ΦωΦ)<おしまい