AtCoder ARC #005 We have nothing to do with him
問題
日本語なので本文参照(http://arc005.contest.atcoder.jp/tasks/arc005_4)
制約条件
priceは10^18以下
使えるボタンに0, 1は必ず含まれている。
方針
動的計画法による。
dp[i][j][k]を、先頭からi桁までを入力していて、
前の桁の余りがjあって、和の形の項の数がk個であるときの、ボタンの押下回数の最小値とする。
(ただし、+, =の回数は含めていない)
このとき、今の桁の数字を合計でいくつにするかで更新ができる。
数字の合計に対して、その合計を作るようなキーの押下回数の最小値は、
これもまた前処理でDPしておいて求める。
DPが終わったら、経路復元する。
j, kは適当に大きく取ったが、最小でいくつまで必要なのかはよくわかってない。
ソースコード
int bs[10], n, d[20], m; int dp[30][300][50]; ll prev[30][300][50], mn[3100], pv[3100]; int ans[100][100]; void make(int ti, int tj, int tk){ if(ti <= 0) return; int l = prev[ti][tj][tk] / 50 / 300; int pj = prev[ti][tj][tk] % (50 * 300) / 50; int pk = prev[ti][tj][tk] % 50; int tmp = l, c = 0; while(tmp > 0){ ans[ti - 1][c++] = bs[pv[tmp]]; tmp -= bs[pv[tmp]]; } make(ti - 1, pj, pk); } int main() { string s, t; cin >> s >> t; n = s.size(); rep(i, n) bs[i] = s[i] - '0'; m = t.size(); rep(i, m) d[i] = t[i] - '0'; rep(i, 30) rep(j, 300) rep(k, 50) dp[i][j][k] = inf; rep(i, 3100) mn[i] = inf; mn[0] = 0; rep(i, 3100) rep(j, n) if(i - bs[j] >= 0 && mn[i] > mn[i - bs[j]] + 1) mn[i] = mn[i - bs[j]] + 1, pv[i] = j; dp[0][0][0] = 0; rep(i, m) rep(j, 300) rep(k, 50) if(dp[i][j][k] != inf){ int num = j * 10 + d[i]; rep(l, 3100){ int nj = num - l; if(nj >= 300 || nj < 0) continue; int use = mn[l], nxt = dp[i][j][k]; int nk = max(k, use); if(use >= 50) continue; nxt += nk; if(dp[i + 1][nj][nk] > nxt) dp[i + 1][nj][nk] = nxt, prev[i + 1][nj][nk] = l * 50ll * 300 + j * 50 + k; } } int a = inf, ansi; rep(i, 50){ int tmp = 0; if(i != 1) tmp += i ? i : 1; if(a > dp[m][0][i] + tmp) a = dp[m][0][i] + tmp, ansi = i; } make(m, 0, ansi); vector<string> res; rep(i, ansi){ ll tmp = 0; rep(j, m) tmp *= 10, tmp += ans[j][i]; stringstream ss; ss << tmp; res.pb(ss.str()); } if(res.empty()) res.pb("0"); if(res.size() == 1) cout << res[0] << endl; else{ rep(i, res.size()) cout << res[i] << (i == (int)res.size() - 1 ? "=\n" : "+"); } return 0; }