今日の精進 - 桁DP (AtCoder EDPC-S, ABC336-E)
AtCoder ABC336のE問題で桁DPの問題が出題されました。問題を読んで「桁DPだな」ということはわかったのですが、実装はできずACできませんでした。そこで、桁DPの復習しました。
まずは桁DPの基本的な問題(EDPC-S)を挑戦
ABC336-Eは私には難しかったので、より基本的な桁DPの問題を探したら、EDPC(Education DP Contest)のS問題が見つかったのでそれをACしてみます。
ポイント
(あんまりよくないかもですが…)桁DPっぽいなぁと思ったら下記の型で考え、各状態について、次の桁の数を10通り試す。
$${\mathrm{dp}[i][j][k] =}$$ 個数
$${\quad i}$$ : $${i}$$ 桁目まで確定している
$${\quad j}$$ : 状態
$${\quad k}$$ : ある数以下か否か上記の問題の場合は、以下の型で各状態について次の桁の数を10通り試す。
$${\mathrm{dp}[i][j][k] =}$$ 個数
$${\quad i}$$ : $${i}$$ 桁目まで確定している
$${\quad j}$$ : 各桁の数字の総和 $${\mathrm{mod} \, \mathrm{D}}$$
$${\quad k}$$ : $${\mathrm{K}}$$以下か否か答えは dp[n][0][0] + dp[n][0][1] - 1。
0 が含まれるので1引く。
実装のポイント
ループも上の型に合わした感じに実装できる。
mint dp[10009][109][2];
...[中略]...
rep(ci, n) {
rep(cj, D) {
rep(ck, 2) {
// 対象の桁の値
int c = K[ci] - '0';
rep(nxt, 10) {
// 配り先の状態などなどを作る
int ni = ci + 1;
int nj = (cj + nxt) % D;
int nk = ck;
// K以下か否かを判定
if (ck == 0) {
if (c < nxt) { continue; }
if (nxt < c) { nk = 1; }
}
dp[ni][nj][nk] += dp[ci][cj][ck];
}
}
}
}
ACしたコード
ABC336-Eを挑戦する
問題はこちら。
ポイント
EDPC-Sの場合は $${\mathrm{D}}$$ が入力1つだけでしたが、ABC336-Eは、$${n}$$ が $${1 \sim 126 \, (=9 \times 14)}$$ まであって、それを全部試す感じの問題。
上記の問題の場合は、以下の型で各状態について次の桁の数を10通り試す。
$${\mathrm{dp}[i][j][k][l] =}$$ 個数
$${\quad i}$$ : $${i}$$ 桁目まで確定している
$${\quad j}$$ : 各桁の和
$${\quad k}$$ : 各桁の和 $${\mathrm{mod} \, \mathrm{n}}$$
$${\quad l}$$ : $${\mathrm{n}}$$以下か否か
イメージとしては、
for(int k = 1; k < 127; k++) {
EDCP-S のコード
}
という感じ。毎回桁DPするイメージ。
ACしたコード
DPしているところはEDPC-Sとさほど変わらない感じでACできました。少し桁DPがわかったような気がします。
次回もがんばります。
この記事が気に入ったらサポートをしてみませんか?