[TC SRM 665 div1 lev1] LuckySum
这道题挺有意思,转化为图来做。顶点v = (carry, zero),其中carry表示是否有从右边一位进1,zero表示是否有其中一个lucky number进入前导0。
G存储从某个顶点通过某边(取某digit)可到达的下一个顶点;E存储某顶点可出去的所有边(即该位可取的digit)。
需要特别注意corner case,当已经遍历到note的最高位,且允许前导0,且有从右边进1时,可以两个lucky number都进入前导0。
1 class LuckySum { 2 public: 3 long long construct(string note) { 4 // [carry][zero] -> digits 5 E[0][0] = {1, 4, 8}; 6 E[0][1] = {4, 7}; 7 E[1][0] = {2, 5, 9}; 8 E[1][1] = {5, 8}; 9 // [carry][zero][digit] -> [carry][zero] 10 G[0][0][1] = {{1, 0}, {1, 1}}; 11 G[0][0][4] = {{1, 0}, {1, 1}}; 12 G[0][0][8] = {{0, 0}, {0, 1}}; 13 G[1][0][2] = {{1, 0}, {1, 1}}; 14 G[1][0][5] = {{1, 0}, {1, 1}}; 15 G[1][0][9] = {{0, 0}, {0, 1}}; 16 G[0][1][4] = {{0, 1}}; 17 G[0][1][7] = {{0, 1}}; 18 G[1][1][5] = {{0, 1}}; 19 G[1][1][8] = {{0, 1}}; 20 G[1][1][1] = {{0, 1}}; // corner case 21 vector<pair<bool, string>> memo(60); // 14 * 4 + 3 + 1; 22 string sum; 23 if (!dfs(note, (int)note.size() - 1, 0, 0, memo, sum)) return -1; 24 long long res = 0; 25 for (auto c : sum) { 26 res = res * 10 + c - '0'; 27 } 28 return res; 29 } 30 31 private: 32 bool dfs(string ¬e, int idx, int c, int z, vector<pair<bool, string>> &memo, string &sum) { 33 if (idx < 0) { 34 sum = ""; 35 return !c; 36 } 37 int key = idx << 2 | c << 1 | z; 38 if (memo[key].first) { 39 sum = memo[key].second; 40 return sum < INF; 41 } 42 sum = INF; 43 string tmp; 44 vector<int> digits = E[c][z]; 45 if (idx == 0 && c && z) digits.push_back(1); // corner case 46 for (auto d : digits) { 47 if (note[idx] != '?' && d + '0' != note[idx]) continue; 48 for (auto &s : G[c][z][d]) { 49 if (dfs(note, idx - 1, s[0], s[1], memo, tmp)) { 50 tmp += d + '0'; 51 if (tmp < sum) sum = tmp; 52 } 53 } 54 } 55 memo[key].first = true; 56 memo[key].second = sum; 57 return sum < INF; 58 } 59 60 private: 61 vector<int> E[2][2]; 62 vector<vector<int>> G[2][2][10]; 63 string INF = "99999999999999"; 64 };