大概了解了什么是数位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说砍树那题是经典题)。