部分和問題・ナップサック問題とほとんど同じ DP で解ける!
問題概要
数直線上で座標 0 から出発して、 回のジャンプをする。
回目のジャンプでは、正の方向に または の移動をする。
回のジャンプの末に座標 に到達することが可能かどうか判定せよ。
制約
解法
部分和問題とほとんど同様に解ける! 部分和問題については次の記事を参照。
この記事の「部分和問題」と同様の DP で解ける。次の配列を考えよう。
dp[i][x]
← 最初から 回のジャンプによって、座標 に到達することが可能かどうかを表すブール値
このとき、DP の更新式は次のように表せる。
x - a[i] >= 0
かつdp[i][x - a[i]] = True
であるとき:dp[i + 1][x] = True
x - b[i] >= 0
かつdp[i][x - b[i]] = True
であるとき:dp[i + 1][x] = True
この DP の計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; int main() { int N, X; cin >> N >> X; vector<int> a(N), b(N); for (int i = 0; i < N; ++i) cin >> a[i] >> b[i]; vector dp(N + 1, vector(X + 1, false)); dp[0][0] = true; for (int i = 0; i < N; ++i) { for (int x = 0; x <= X; ++x) { if (x - a[i] >= 0 && dp[i][x - a[i]]) dp[i + 1][x] = true; if (x - b[i] >= 0 && dp[i][x - b[i]]) dp[i + 1][x] = true; } } cout << (dp[N][X] ? "Yes" : "No") << endl; }