HDOJ 3555 Bomb 数位dp

http://acm.hdu.edu.cn/showproblem.php?pid=3555

题意:问从0到n,有多少个数含有49。

分析:集训的时候做的,没来及写解题报告,刚好这两天又写了几个数位dp,顺带补了。

dp[i][0]表示长度为i的数串,含49的个数。

dp[i][1]表示长度为i的数串,不含49,且第一位为9的个数。

dp[i][2]表示长度为i的数串,不含49,且第一位不为9的个数。

递推过程见代码。

然后对于给定的n,从高位往下枚举,已经枚举过的位默认为该位最大值。当前位最大值为x,我们就算填0..x-1的方案,首先有x*dp[i][0],即这位填0..x-1乘以之后位含49的方案。如果这一位大于4,我们可以填4,下一位填9。如果这位和之前一位组成了49,那么之后0..之后最大值都是含49的,加上就可以。

 

网上还有一种类似做法,大同小异,不过更好理解。递推长度为i,含49,不含49,不含49且首位为9的个数。也从高到低枚举,首先加上这位任填,之后有49的方案数,如果之前已经有49了,那么这位任填,之后不含49的也可以加上。如果之前没有49,可以加上这位填4,之后补一个9的方案数。

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 int T;
 7 int a[40];
 8 long long dp[40][3];
 9 long long n;
10 int main()
11 {
12     memset(dp, 0, sizeof(dp));
13     dp[0][2] = 1;
14     for (int i = 1; i <= 24; i++){
15         dp[i][0] = dp[i-1][0] * 10 + dp[i-1][1];    //i-1长度有49,第i位随便填,或者在i-1且最高位为9的前面补一个4
16         dp[i][1] = dp[i-1][1] + dp[i-1][2];       //i-1不含49的两种加起来,都是在前面补9
17         dp[i][2] = dp[i-1][1] * 8 + dp[i-1][2] * 9;  //i-1最高位为9,则这位不能填4和9,不为9,不能填9
18     }
19     scanf("%d", &T);
20     while(T--)
21     {
22         scanf("%I64d", &n);
23         int len = 0;
24         while(n){
25             a[len++] = n % 10;
26             n = n / 10;
27         }
28         a[len] = 0;
29         long long ans = 0;
30         for (int i = len-1; i+1; i--){
31             if (a[i] == 0) continue;
32             ans = ans + dp[i][0] * a[i];
33             if (a[i] > 4) ans = ans + dp[i][1];
34             if (a[i+1] == 4 && a[i] == 9){
35                 long long tmp = 0;
36                 for (int j = i-1; j+1; j--)
37                     tmp = tmp * 10 + a[j];
38                 ans = ans + tmp + 1;
39                 break;                //漏掉break就会重复计算了
40             }
41         }
42         printf("%I64d\n", ans);
43     }
44     return 0;
45 }

 

posted @ 2014-08-29 18:04  james47  阅读(222)  评论(0编辑  收藏  举报