SRM 665 Div2 Hard LuckySum
TopCoder Statistics - Problem Statement
- 復習。復習。
- 最後の桁と最初の桁だけ例外判定が必要。
- こういう dp はまだまだパッと思いつかない。
class LuckySum { public: // // a,b を 1桁目から確定させていく。 // 各桁は 0,4,7 の組み合わせ(0 は leading zero 、つまり a,b の桁数が異なるケース)と // 前の桁の繰り上がりとの和で決定する。 // よって状態として 桁数とくりあがりの有無と leading zero の有無の (n,u,z) となる。 // // dp[n][u][z] := a+b の n 桁目までで条件を満たすものの最小値 // u==1 のときは桁上がりがある。 // z==0 のときは a, b ともにまだ leading zero でない // z==1 のときは a は leading zero // z==2 のときは b は leading zero // // として DP すればよい。 // long long construct(string note) { int len = note.length(); const ll inf = (1LL<<60); // 1 桁目からみていくので逆順にしておく reverse(RANGE(note)); // 10^k 倍用に用意 vector<ll> p10(len+1,0); p10[0] = 1; REP(i,len) p10[i+1] = 10*p10[i]; ll dp[len+1][2][3]; REP(i,len+1) REP(j,2) REP(k,3) dp[i][j][k] = inf; dp[0][0][0] = 0; for (int l = 0; l < len; ++l) REP(u,2) REP(z,3) { // そこまで遷移不可なら飛ばす if (dp[l][u][z]==inf) continue; const int lucky[] = {0,4,7}; REP(a,3) REP(b,3) { // 0 桁目が leading zero はダメ if ( l==0 && (a==0 || b==0)) continue; // すでに leading zero なら使えるものは 0 のみ if ( z==1 && a!=0 ) continue; if ( z==2 && b!=0 ) continue; // 両方 leading zero がゆるされるのは if ( a==0 && b==0 ) { // 最後の桁で繰り上がりがあるときのみ if ( !(l+1==len && u==1) ) continue; } // 0,4,7 を使って n 桁目を足し合わせる。このとき繰り上がり分 u も加える int num = lucky[a]+lucky[b]+u; // 指定されている数と違ったら飛ばす if (note[l] != '?' && num%10 != note[l]-'0') continue; int nz = 0; // この桁で使われるのが 0 なら次の桁以降で leading zero になるはず。 if (z == 1 || a == 0) nz = 1; if (z == 2 || b == 0) nz = 2; // 更新 dp[l+1][num/10][nz] = min(dp[l+1][num/10][nz], (num%10)*p10[l]+dp[l][u][z]); } } ll res = inf; REP(z,3) { res = min(res, dp[len][0][z]); } if (res == inf) return -1; return res; } };