ABC262 D - I Hate Non-Integer Number Python解答例
AtCoder Beginner Contest 262 D - I Hate Non-Integer NumberをPythonで解きます。
問題
問題文をAtCoderのページより引用します。
問題文
項数が
の正整数列 N が与えられます。 A=(a_1,\ldots,a_N)
の項を A 個以上選ぶ方法は 1 通りありますが、そのうち選んだ項の平均値が整数であるものが何通りかを 2^N-1 で割った余りを求めてください。 998244353 制約
1 \leq N \leq 100 1 \leq a_i \leq 10^9 - 入力はすべて整数
解答例
動的計画法
ここで、関心があるのは
また、
なんとなく動的計画法で解ける気がします。
DPテーブルの定義
整数
ただし、
初期条件は
遷移は、
-
を選ばない場合:a_i dp[i][j][k] += dp[i - 1][j][k] -
を選ぶ場合:a_i dp[i][j][k] += dp[i - 1][j - 1][k - a_i \mod l] \quad (k - a_i \geq 0)
この問題の解は、全ての
遷移の解釈
-
を選ばない場合、a_i 項までを選択対象にしたときの結果から変動しないので、結果をそのまま足す。i - 1 -
を選ぶ場合、「a_i を選んだことにより、選んだ項の個数数がa_i になり、総和をj で割った余りがl になる」場合の数は、k の場合の数に等しいため、これを足す。dp[i - 1][j - 1][k - a_i]
DPテーブルの定義について補足
この解法では、1以上
「以下のようなDPテーブル1つで解が求まるんじゃないか」と一瞬考えますが、それはできないことがすぐにわかります。
このDPテーブルで遷移を考えたとき、
このときの場合の数は、選んだ項の個数が
なので、選んだ数の総和を割る数を
(
実装例
Pythonによる実装例を以下に示します。PyPy3で提出しないとTLEします。
(k - a % l) % l
と書くことができます。
from typing import List
def main():
# 入力受け取り
N: int = int(input())
A: List[int] = list(map(int, input().split()))
mod: int = 998244353
answer: int = 0
# 1以上N以下の整数lについてそれぞれ動的計画法で解を求める
for l in range(1, N + 1):
# DPテーブル
# 内側の次元のリストの長さを制限しないとTLEするので注意
dp: List[List[List[int]]] = [
[[0 for k in range(l)] for j in range(l + 1)] for i in range(N + 1)
]
# 初期条件
for i in range(N + 1):
dp[i][0][0] = 1
# 遷移
for i in range(1, N + 1):
for j in range(1, l + 1):
for k in range(l):
# i番目の要素を選ばない場合
dp[i][j][k] += dp[i - 1][j][k]
# i番目の要素を選ぶ場合
dp[i][j][k] += dp[i - 1][j - 1][(k - (A[i - 1] % l)) % l]
# 余りを取る
dp[i][j][k] %= mod
# 解を足し合わせる
answer += dp[N][l][0] % mod
print(answer % mod)
if __name__ == "__main__":
main()
実際の提出はこちら。
(PyPy3でも実行制限時間ギリギリなのでもうちょっと改善の余地がありそうです。)
Discussion