参考:http://www.cnblogs.com/liuxueyang/archive/2013/04/14/3020032.html
题意:给一个数字N,求1到N中含有49的数的个数
分析:经典数位dp。
首先定义状态:
dp[i][0]代表长度为 i 并且不含有49的数字的个数;
dp[i][1]代表长度为 i 并且不含有49,但是最高位是9的数字的个数;
dp[i][2]代表长度为 i 并且含有49的数字的个数。
转移:
dp[i][0] = dp[i-1][0] a[i] - dp[i-1][1]; 表示长度为 i 的不含有49的数字的个数等于长度为 i - 1 的不含有49的数字的个数当前的数字,因为这个位置可以填0~a[i] - 1,然后再减去长度为 i - 1 的最高位是9的数字的个数,因为如果长度为 i - 1 的最高位是9的话,那么高一位就不能填4了,否则就组成了49。
dp[i][1] = dp[i-1][0]; 表示长度为 i 的并且不含有49同时最高位是9的数字的个数等于,长度为 i - 1 的不含有49的数字的个数,因为只要在它的高一位加上一个9就可以了。
dp[i][2] = dp[i-1][2] a[i] + dp[i-1][1]; 表示长度为 i 的含有49的数字的个数等于,长度为 i - 1 的数字的个数当前的数字,再加上长度为 i - 1 的并且不含有49同时最高位是9的数字的个数,因为这个时候,只要在高一位加上一个4就可以了,这样在最高的两位就组成了一个49。
以上是预处理。
接下来步骤:从高位往低位扫描,例如第i位
- 首先加上 dp[i-1][2]*a[i]
- 若前面是否出现过49,加上 dp[i-1][0]*a[i]
- 若为出现49且a[i]>4,说明可以在这个位填上4,那么加上dp[i-1][1]
- 此刻形成49了,做标记。
其他看代码:
12345678910111213141516171819202122232425262728293031323334353637383940414243 | #include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <algorithm>using namespace std;typedef long long int LL;LL dp[21][3]; unsigned long long int n; int a[25];int (){ int t; scanf("%d", &t); memset(dp, 0, sizeof(dp)); dp[0][0] = 1; for (int i = 1; i < 21; ++i){ dp[i][0] = dp[i-1][0] * 10 - dp[i-1][1]; dp[i][1] = dp[i-1][0]; dp[i][2] = dp[i-1][2] * 10 + dp[i-1][1]; } while (t--){ scanf("%I64d", &n); int len = 0; memset(a, 0, sizeof(a)); n++; while (n){ a[++len] = n % 10; n /= 10; } LL ans = 0; int last = 0; bool flag = false; for (int i = len; i >= 1; --i){ ans += (dp[i-1][2] * a[i]); if (flag) ans += dp[i-1][0] * a[i]; if (!flag && a[i] > 4) {ans += dp[i-1][1];} if (last == 4 && a[i] == 9) {flag = true;} last = a[i]; } printf("%I64dn", ans); } return 0;} |