您的位置:首页 > 其它

[数位dp] hdu 3555 Bomb

2014-09-05 12:06 204 查看
题意:求1~n中,带有49的数字有多少个。 4949算一个数字。

思路:设dp[i][j]代表 在i位数首位是j之前,含有49的数有多少个

那么很明显 dp[i][5]的时候 会出现一个显著的增长

拿dp[4][5]举例

因为 4000~4999 中间会有49开头的数这些数每个都是 1000个

其实就是有 10^(i-2)个 但是会有重复。

重复的就是原来去掉49之后 后面的 0~99 之间含有49的个数

这里的就是4949这个算了2次要减掉。

所以状态转移方程就是 dp[i][j]=dp[i][j-1]+dp[i-1][10]

如果j==5要特殊考虑

dp[i][j]=dp[i][j-1]+dp[i-1][10]+10^(i-2)-dp[i-2][10]

初始化完之后就是求了

求的时候一切都正常 就是如果出现49开头的时候

比如49xxx

那么我们要加上 (49xxx%1000+1) 这是以49开头后面的数

但是还要减去 xxx所含有的49的个数 因为这些重复计算了

减去的其实就是一个递归 再算一次 具体见带代码

代码:

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#define eps 1e-8
using namespace std;
__int64 dp[22][12];
__int64 ten[22];
__int64 slove(__int64 a)
{
    if(a<49) return 0;
    if(a<100) return 1;
    __int64 len,n,t=1,ans=0;
    len=(__int64)log10(a*1.0)+1;
    n=len-1;
    while(n--) t*=10;
    while(len)
    {
        __int64 tep=a/t;
        if(len==1) tep++;
        if(len>=2&&a/(t/10)==49) ans+=a%(t/10)+1-slove(a%(t/10));  //注意要减掉
        ans+=dp[len][tep];
        len--;
        a%=t;
        t/=10;
    }
    return ans;
}
int main()
{
    int i,j;
    memset(ten,0,sizeof(ten));
    ten[2]=1;
    for(i=3;i<=20;i++) ten[i]=ten[i-1]*10;
    memset(dp,0,sizeof(dp));
    for(i=2;i<=20;i++)
    {
        for(j=1;j<=10;j++)
        {
            dp[i][j]+=dp[i][j-1]+dp[i-1][10];
            if(j==5)
            {
                dp[i][j]+=ten[i];
                dp[i][j]-=dp[i-2][10];  //去重
            }
        }
    }
    int t;
    cin>>t;
    while(t--)
    {
        __int64 n;
        scanf("%I64d",&n);
        printf("%I64d\n",slove(n));
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: