现在的位置: 首页 > 综合 > 正文

[HDU 3555]Bomb[数位DP]

2019年04月05日 ⁄ 综合 ⁄ 共 1205字 ⁄ 字号 评论关闭

题意:

给出一个数N,求从1到N中含49的数的个数.

思路:

按位DP.

dp[i][0] i bits num including 49
dp[i][1] i bits num excluding 49 but heading with 9
dp[i][2] i bits num excluding 49

转移方程

dp[i][0] = dp[i-1][0]*10 + dp[i-1][1];
dp[i][1] = dp[i-1][2];
dp[i][2] = dp[i-1][2]*10 - dp[i-1][1];

关于00049,0049,049重复的问题:

因为是预处理,所以不代表是合法的数字,只是代表数字序列.

计算结果时,最高位也是从0开始的.只有计算最高位的时候是将不足len位的数全部算进去,循环到len位之下的时候,看似i在减小,讨论的数其实是在增加(默认第i+1位填上了原数).因此每一位计算时,最高位都应该从0开始取.

dp数组初始化的时候,dp[0][2] = 1.这个是观察出来的...只有这样i=1之后的数才能根据转移方程计算正确.就像0!=1一样,这样规定之后也是合理的.

另外,由于计算结果的时候只计入<N的数,故输入的N要++.

#include <cstdio>
using namespace std;
typedef long long ll;
const int MAXN = 20;
ll dp[MAXN][3];
/*
dp[i][0] i bits num including 49
dp[i][1] i bits num excluding 49 but heading with 9
dp[i][2] i bits num excluding 49
*/
int bit[MAXN];
void InitDP()
{
    dp[0][2] = 1;///初始化!T^T
    dp[0][1] = dp[0][0] = 0;
    for(int i=1;i<MAXN;i++)
    {
        dp[i][0] = dp[i-1][0]*10 + dp[i-1][1];
        dp[i][1] = dp[i-1][2];
        dp[i][2] = dp[i-1][2]*10 - dp[i-1][1];
    }
}
void pre(ll x,int &len)
{
    int i;
    for(i=1;x;i++)
    {
        bit[i] = x%10;
        x /= 10;
    }
    bit[i] = 0;
    len = i-1;
}

int main()
{
    int T;
    scanf("%d",&T);
    InitDP();
    while(T--)
    {
        ll x, ans = 0;
        int len;
        bool flag = 0;
        scanf("%I64d",&x);
        x++;
        pre(x,len);
        for(int i=len;i;i--)
        {
            ans += dp[i-1][0]* bit[i];
            if(flag)   ans += dp[i-1][2]* bit[i];
            if(!flag && bit[i]>4)   ans += dp[i-1][1];
            if(bit[i+1]==4 && bit[i]==9)    flag = 1;
        }
        printf("%I64d\n",ans);
    }
}

抱歉!评论已关闭.