这题的状态真的是很难想到,网上的代码都惊人的相似...
另一种解法,相比而言好接受一点:
#include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> using namespace std; typedef unsigned long long Int64; Int64 dp[20][3], N; int digit[20]; Int64 dfs(int pos, int statu, int limit) { if (pos == -1) { // 如果到了已经枚举了最后一位,并且在枚举的过程中有49序列出现 return statu == 2; } if (!limit && dp[pos][statu] != -1) return dp[pos][statu]; // 对于有限制的询问我们是不能够记忆化的 Int64 sum = 0; int s, end = limit ? digit[pos] : 9; // 确定这一位的上限是多少 for (int i = 0; i <= end; ++i) { // 每一位有这么多的选择 s = statu; // 有点else s = statu 的意思 if (statu == 1 && i == 9) s = 2; if (statu == 0 && i == 4) s = 1; if (statu == 1 && i != 4 && i != 9) s = 0; sum += dfs(pos-1, s, limit && i == end); } if (!limit) dp[pos][statu] = sum; return sum; } Int64 Cal(Int64 x) { int len = -1; while (x != 0) { digit[++len] = x % 10; x /= 10; } return dfs(len, 0, 1); } int main() { int T; scanf("%d", &T); while (T--) { memset(dp, 0xff, sizeof (dp)); scanf("%I64u", &N); printf("%I64u\n", Cal(N)); } return 0; }
详见代码:
#include <cstdlib> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef unsigned long long int Int64; Int64 N, dp[25][3]; int digit[25]; /* dp[len][0] 表示前len位没有49的数字的个数 dp[len][1] 表示前len位没有49但是以9结尾的数的个数 dp[len][2] 表示前len位有49的状态 */ void pre() { dp[0][0] = 1; for (int i = 1; i <= 20; ++i) { dp[i][0] = 10 * dp[i-1][0] - dp[i-1][1]; dp[i][1] = dp[i-1][0]; dp[i][2] = 10 * dp[i-1][2] + dp[i-1][1]; } } int main() { pre(); int T, len, flag; Int64 ret; scanf("%d", &T); while (T--) { flag = ret = 0; scanf("%I64u", &N); ++N; memset(digit, 0, sizeof (digit)); for (len = 1; N; ++len) { digit[len] = N % 10; N /= 10; } for (int i = len-1; i >= 1; --i) { ret += digit[i] * dp[i-1][2]; // 已经有49就直接加上 if (flag) { ret += digit[i] * dp[i-1][0]; } else if (!flag && digit[i] > 4) { ret += dp[i-1][1]; } if (digit[i+1] == 4 && digit[i] == 9) { flag = 1; } } printf("%I64u\n", ret); } return 0; }