动态规划学习系列——数位DP(练手一)

时间:2020-12-31 20:41:51

大概了解了什么是数位DP,想做几道题练练手,于是找到了这道题:
题目链接:hdu 2089 不要62
题目大意是统计【A,B】区间内没有4并且没有62的数,因为有之前那道题的铺垫,很快想到了解决方法。
思路: 预处理一个dp数组,dp【i,j】代表最高位为 j 的 i 位数满足题述要求的的个数;然后分别统计【0,B】和【0,A-1】区间内满足条件的数——与之前那题类似,不过更简单了,具体来看代码吧:
代码

#include <cstdio>

using namespace std;

int n,m,dp[10][10],num[10],cnt,ans;

int solve(int n)
{
    cnt=0,ans=0;
    while(n){                 //处理出n的每一位
        num[++cnt]=n%10;
        n/=10;
    }
    num[cnt+1]=0;

    for(int k=cnt;k>=1;k--)
    {
        for(int i=0;i<num[k];i++)
        {
            if(i!=4&&!(num[k+1]==6&&i==2))
                ans+=dp[k][i];
        }
        if(num[k]==4||(num[k+1]==6&&num[k]==2))
            break;
    }
    return ans;
}

int main()
{
    dp[0][0]=1;
    for(int i=1;i<=9;i++)
        for(int j=0;j<=9;j++)
            for(int k=0;k<=9;k++)
                if(j!=4&&!(j==6&&k==2))
                    dp[i][j]+=dp[i-1][k];
    while(scanf("%d %d",&n,&m)&&n&&m)
    {
        printf("%d\n",solve(m+1)-solve(n));
    }

    return 0;
}

从代码看,我们求解【0,B】区间内符合条件的数的方法依旧类似,从最高位(k = cnt)开始,每次取一位,统计最高位为 i 的数的个数(0 <= i <= num[k]),于是变成下面的代码:

for(int k=cnt;k>=1;k--)
{
    for(int i=0;i<num[k];i++)
    {
        if(i!=4&&!(num[k+1]==6&&i==2))
            ans+=dp[k][i];
    }
    if(num[k]==4||(num[k+1]==6&&num[k]==2))
        break;
}

这里有一个需要注意的地方:如果之前的的数位已经出现不符合的情况了,那么我们就不应该继续统计下去!(因为这个WA了一次)

总结
理解过上一道数位DP之后,看这道题并不会那么吃力了,其实两道题本质上是差不多的,只是最后进行数位统计的部分有点不同(难怪当时TA说砍树那题是经典题)。