题目链接:CodeForces 1567C Carrying Conundrum
题目大意:
小红把加法列竖式时的进位进到了该列左边的第二列,得到了错误的结果,给定一个错误的结果,求可以得到这个错误结果的加数对。
题解:
做法一:(思维)
由于进位进到了左边第二列,所以每隔一列的加法结果是正确的,因此可以把这个错误的结果按照奇数位和偶数位分成两个正确的结果,再分别求出他们的加数对数量,相乘即为结果。
对于求一个数\(n\)的加数对个数,显然可知有\(n + 1\)对,分别为\(n+0\)、\((n-1)+1\)、\((n-2)+2\)、...、\(0+n\)。
由于当两组加数对的第一个加数或第二个加数都为\(0\)时,结果会重复,所以总结果要减去\(2\)。
#include <iostream>
#include <string>
using namespace std;
int main() {
int t; cin >> t;
while (t--) {
string s, s1, s2;
cin >> s;
for (int i = 0; i < s.length(); ++i) {
if (!(i & 1)) s1 += s[i];
else s2 += s[i];
}
if (s2.empty()) cout << stoi(s1) - 1 << endl;
else cout << (stoi(s1) + 1) * (stoi(s2) + 1) - 2 << endl;
}
return 0;
}
做法二:(动态规划)
设一个数从左到右依次为第\(0\)、\(1\)、\(2\)...位。
设\(dp[i][j][k]\)表示当前为第\(i\)位,且第\(i-1\)位进位为\(j\),第\(i-2\)位进位为\(k\)的情况数(\(j,k\in \{0,1\}\),表示是否有低位的进位)。
则从最低位到最高位依次枚举该列所有可能的结果并迭代,最终结果即为\(dp[0][0][0]-2\)。
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int t, n, dp[11][2][2];
string s;
int main() {
cin >> t;
while (t--) {
cin >> s;
n = s.length();
memset(dp, 0, sizeof(dp));
dp[n][0][0] = 1;
for (int i = n - 1; i >= 0; --i) {
for (int j = 0; j < 10; ++j) {
for (int k = 0; k < 10; ++k) {
for (int l = 0; l < 2; ++l) {
for (int m = 0; m < 2; ++m) {
if ((j + k + l) % 10 == s[i] - ‘0‘) {
dp[i][m][(j + k + l) / 10] += dp[i + 1][l][m];
}
}
}
}
}
}
cout << dp[0][0][0] - 2 << endl;
}
return 0;
}