2020-06-23
【Python】ABC169 F - Knapsack for All Subsets 解説
目次
ABC169 F問題, コンテスト中には解けなかったので解説AC.
考察:難, 実装:易の問題ですね.
問題
問題ページはこちら
題意
公式解説より引用.
の空でない部分集合の組であって以下の条件を満たすものは何通りありますか?
考え方
各について3つの選択肢がある
普通のナップサック問題であれば「ナップサックに入れる/入れない」の2つの選択肢だけだが, 本問では以下の3つの選択肢がある.
- にもにも入れない
- に入れるが, に入れない
- に入れる
これらをの左から順に場合分けして数え上げていくと, 以下のような遷移をすることがわかる.
ポイントは, スタート と の遷移が独立となっている点である. つまり, 例えばを見るとのすべてのから3通りすべての遷移が行われている.
この性質によりdpで解くことができる.
解答
以上の考察により, 結局は典型的なDP問題に帰着する.
# pypyでないとTLE
N, S = map(int, input().split())
A = list(map(int, input().split()))
MOD = 998244353
# dp[i][j] := i番目までの遷移をして, Uの要素の和が j であるような場合の数
dp = [[0] * (S + 1) for _ in range(N + 1)]
dp[0][0] = 1
for i in range(N):
ai = A[i]
for j in range(S + 1):
here = dp[i][j]
# 1. TにもUにも入れない
dp[i + 1][j] += here
# 2. Tに入れるが, Uに入れない
dp[i + 1][j] += here
dp[i + 1][j] %= MOD
# 3. Uに入れる
if j + ai <= S:
dp[i + 1][j + ai] += here
dp[i + 1][j + ai] %= MOD
# 答えは, N番目まで見終わったときにUの合計=Sとなっている場合の数
print(dp[N][S])
なお, この問題では の遷移しか無いため, dp配列 を1次元で持つことができる. 当然こちらの方が速い.
# pypyでないとTLE
N, S = map(int, input().split())
A = list(map(int, input().split()))
MOD = 998244353
dp = [0] * (S + 1) # DP配列は1次元
dp[0] = 1
for i in range(N):
ai = A[i]
for j in range(S, -1, -1): # 重複して数え上げないように大きい方からループする
dp[j] = (2 * dp[j] + (j - ai >= 0) * dp[j - ai]) % MOD
print(dp[S])
関連記事最新記事
2021-05-09【Python】AtCoder - 30代社会人が始めて1年半で青色になりました
2021-04-12【Python】ABC198 解説
2021-03-27【Python】ABC197 解説
2021-03-20【Python】ABC196 解説
2021-03-13【Python】ABC195 パナソニックプログラミングコンテスト 解説