2次元の動的計画法まとめ 部分和問題編
動的計画法を用いて解くことができる問題のうち、部分和問題とその亜種問題の解法を自分用にまとめる。
基本の部分和問題
問題
配列
配列
DPテーブル
DPテーブルの定義
初期条件
その他の要素は偽としておく。
漸化式
-
番目の要素を選ばないi -
番目の要素を(選べるならば)選ぶi
の2通りが考えられる。
これらを踏まえると、漸化式は以下のようになる。
出力
例題と解答例
例題
解答例
アルゴ式 部分和問題の C++解答例のソースコードを以下に示す。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
int main() {
// 入力受け取り
int64_t N, M;
std::cin >> N >> M;
std::vector<int64_t> A(N + 1);
for (int64_t i = 1; i <= N; i++) {
std::cin >> A.at(i);
}
// DPテーブル
std::vector<std::vector<bool>> dp(N + 1, std::vector<bool>(M + 1, false));
// 初期条件
for (int64_t i = 0; i <= N; i++) {
dp.at(i).at(0) = true;
}
// 遷移
for (int64_t i = 1; i <= N; i++) {
for (int64_t j = 1; j <= M; j++) {
// i番目の要素を選ばないとき
dp.at(i).at(j) = dp.at(i).at(j) || dp.at(i - 1).at(j);
// i番目の要素を選ぶとき
if (j - A.at(i) >= 0) {
dp.at(i).at(j) = dp.at(i).at(j) || dp.at(i - 1).at(j - A.at(i));
}
}
}
// 出力
if (dp.at(N).at(M)) {
std::cout << "Yes" << std::endl;
} else {
std::cout << "No" << std::endl;
}
return 0;
}
実際の提出結果はこちら。
個数制限あり部分和問題
問題
配列
配列
ただし、整数
DPテーブル
DPテーブルの定義
総和をちょうど
初期条件
漸化式
-
番目の要素を1つも使わない(使う必要が無い)i -
番目の要素を何個使っても総和をちょうどi にできないj -
番目の要素をいくつか使って総和をちょうどi にできるj
の3通りが考えられる。
逆に、
また、
最後に、
以上を踏まえると、漸化式は以下のようになる。
出力
No
、そうでなければYes
を出力する。
例題と解答例
例題
解答例
アルゴ式 個数制限付き部分和問題のC++解答例ソースコードを以下に示す。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
int main() {
int64_t N, M;
std::cin >> N >> M;
std::vector<int64_t> A(N);
std::vector<int64_t> B(N);
for (int64_t i = 0; i < N; i++) {
std::cin >> A.at(i) >> B.at(i);
}
// DPテーブル
std::vector<std::vector<int64_t>> dp(N + 1, std::vector<int64_t>(M + 1, -1));
// 初期条件
dp.at(0).at(0) = 0;
// 遷移
for (int64_t i = 1; i <= N; i++) {
for (int64_t j = 0; j <= M; j++) {
if (dp.at(i - 1).at(j) >= 0) {
// i番目の要素を使わずに済む場合
dp.at(i).at(j) = B.at(i - 1);
} else if (j < A.at(i - 1) || dp.at(i).at(j - A.at(i - 1)) <= 0) {
// i番目の要素を何個使っても総和をちょうどjにできない
dp.at(i).at(j) = -1;
} else {
// i番目の要素をいくつか使って総和をちょうどjにできる
dp.at(i).at(j) = dp.at(i).at(j - A.at(i - 1)) - 1;
}
}
}
// 出力
if (dp.at(N).at(M) >= 0) {
std::cout << "Yes" << std::endl;
} else {
std::cout << "No" << std::endl;
}
return 0;
}
実際の提出結果はこちら。
参考文献
- 秋葉拓哉、岩田陽一、北川 宜稔、問題解決のアルゴリズム活用力とコーディングテクニックを鍛える プログラミングコンテストチャレンジブック 第2版、株式会社マイナビ出版、2021年4月12日
Discussion