hdu 3555 Bomb (数位DP)

来源:互联网 发布:java开发网上商城 编辑:程序博客网 时间:2024/05/29 16:28

题目大意

给出一个数字N,求出从1到N的数中含有字串49的串的个数。

解题思路

我们可以按照递推的思想来构造,逐步求解,长度为i的串可以由长度为i-1的串在构造得到,由低位到高位构造求解
设置一个二维数组dp用来保存状态。dp[i][0]表示长度为i且不含有字串49的串的个数,dp[i][1]代表长度为i不含有字串49且以9开头的串的个数,dp[i][2]代表长度为i且含有字串49的串的个数。
可以列出状态转移方程为:
dp[i][0]=dp[i-1][0]*10-dp[i-1][1];
//在长度为i-1的串基础上,在首位前增加一位数字 0-9都满足条件,但是要减去一种情况:原串的首位为9且新增位数字为4。这里要注意dp[i][0]是包括dp[i][1]的,也就是说首位为9的不含49字串的数目也包含在dp[i][0]中。
dp[i][1]=dp[i-1][0];
//新增的以9开头的串可由原串基础上增加一位数字9得到。由于dp[i-1][0]中已经包含了以9开头的串,所以不需要再加上dp[i-1][1];
dp[i][2]=dp[i-1][2]*10+dp[i-1][1];
//长度为i且含49的串可以由长度为i-1且含49(dp[i-1][2]*10)前增加任意数字获得,也可以由原9开头的不含49串在加上首位4(dp[i-1][1])获得;

下面来讨论详细的过程:
1.将n的各位分别存入数组中
2.例如 47349
高位为0-3时,长度为4的串都能讨论到(0-39999 get)
高位为4时,下一位为0-6时,长度为3的串都能讨论到(40000-46999 get),这里要注意有一种特殊情况被忽略了,当高位的下一位大于4时,会有 449XX 的情况都满足题意但是却没有被讨论到。
高位为47时,下一位为0-2,长度为2的串都能讨论到(47000-47299 get),由于高位的下一位不大于4,故不存在 4749X 的情况。
高位为473时,下一位为0-3时,长度为1的串都能被讨论到(47300-47339 get)。但是由于有47349的存在,所以最终结果要加上这种情况。也可以通过对n+1的方式避免讨论n的最低两位为49的情况。

附上代码

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef long long int ll;ll dp[25][3],t,num[1000];ll n;void init(){    memset(dp,0,sizeof(dp));    dp[0][0]=1;    for(int i=1;i<=20;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];    }}void solve(){    ll ans=0;    int cont=0,flag=0;    memset(num,0,sizeof(num));    n++;    while(n)    {        num[++cont]=n%10;        n/=10;    }    for(int i=cont;i>=1;i--)    {        ans+=num[i]*dp[i-1][2];        if(flag)            ans+=num[i]*dp[i-1][0];        else if(num[i]>4)            ans+=dp[i-1][1];        if(num[i+1]==4&&num[i]==9)            flag=1;    }    printf("%lld\n",ans);}int main(){    init();    scanf("%lld",&t);    while(t--)    {        scanf("%lld",&n);        solve();    }    return 0;}
0 0
原创粉丝点击