您的位置:首页 > 其它

HDOJ 3555 Bomb 数位dp

2014-08-29 18:04 351 查看
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的方案数。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int T;
int a[40];
long long dp[40][3];
long long n;
int main()
{
memset(dp, 0, sizeof(dp));
dp[0][2] = 1;
for (int i = 1; i <= 24; i++){
dp[i][0] = dp[i-1][0] * 10 + dp[i-1][1];    //i-1长度有49,第i位随便填,或者在i-1且最高位为9的前面补一个4
dp[i][1] = dp[i-1][1] + dp[i-1][2];       //i-1不含49的两种加起来,都是在前面补9
dp[i][2] = dp[i-1][1] * 8 + dp[i-1][2] * 9;  //i-1最高位为9,则这位不能填4和9,不为9,不能填9
}
scanf("%d", &T);
while(T--)
{
scanf("%I64d", &n);
int len = 0;
while(n){
a[len++] = n % 10;
n = n / 10;
}
a[len] = 0;
long long ans = 0;
for (int i = len-1; i+1; i--){
if (a[i] == 0) continue;
ans = ans + dp[i][0] * a[i];
if (a[i] > 4) ans = ans + dp[i][1];
if (a[i+1] == 4 && a[i] == 9){
long long tmp = 0;
for (int j = i-1; j+1; j--)
tmp = tmp * 10 + a[j];
ans = ans + tmp + 1;
break;                //漏掉break就会重复计算了
}
}
printf("%I64d\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: