hdu 2089 不要62 数位dp

时间:2022-12-16 09:03:41
杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。 
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。 
不吉利的数字为所有含有4或62的号码。例如: 
62315 73418 88914 
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。 
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。 
Input
输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。 
Output
对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。 
Sample Input
1 100
0 0
Sample Output

80

思路:典型的数位dp的问法;

数位dp适合在一段数的区间内找出满足某些条件的数的个数,这个时候往往不能之间遍历,肯定会超时,则一般使用数位dp来解决

dp[i][j]表示以j为首位,位数为i位的数字中有多少满足要求的

那么我们很容易得到 当j=4时 dp[i][j]=0

当j!=4时, hdu 2089 不要62 数位dp

如何根据数组dp求对于一个数n,0~n满足要求的数呢?

对于一个数,例如335,对应dp[3][3],但是满足要求的数有336,347,358……这些数超过了335……

所以通过dp[3][0],dp[3][1],dp[3][2]求得首位数字小于3的满足条件的三位数,dp[3][0]求得的数是001,052,093...也就是所有满足条件的一位或两位数……我们求得所有的2xx,1xx,0xx.

接下来要求的是如334,327这类首位是3满足条件的数字,既然首位只能是3,也就是第一位已经选完了,那么我们只要选择满足小于35的数字就可以了。和上面同理,我们只要求出dp[2][0],dp[2][1],dp[2][2],dp[2][3],dp[2][4]就可以求出首位小于3的两位数。这时求得的是32x,31x,30x。

然后我们求所有小于5的数字就ok。小于5也就是dp[0],dp[1],dp[2],dp[3],dp[4]。这时求得的是53x.

到此,所有小于335且满足的数全部求出。

 

但是还需要考虑的问题是62,4。

如果某一位数字为4,如77432的第三位(从左向右看)是4,那么我们依次求出6xxxx,5xxxx,...,0xxxx,76xxx,75xxx,...,70xxx,773xx,773xx,...,770xx,我们接来下需要求的是7742x....7740x,但这时所求的数是774xx的数,必然含有4,所以这时我们停止。

对于62,如果某一位数字是6,如5675,我们求完5xxx,4xxx,...0xxx之后再求56xx时,注意566x,565x,..,563x,561x,没有562x。如果数字中含有62,如5627,那么同4,求到562x的时候就应该停止继续求下去了。

 

因为求得的是小于n的数,所以求[0,n]之间的数需要求n+1.


#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[10][10];
int d[10];
void init()
{
int i,j,k;
dp[0][0]=1;
for(i=1;i<=7;i++)
for(j=0;j<=9;j++)
for(k=0;k<=9;k++)
{
if(j!=4&&!(j==6&&k==2))
dp[i][j]+=dp[i-1][k];
}
return ;
}
int cal(int x)
{
int len=0,i,j,ans=0;
while(x)
{
d[++len]=x%10;
x/=10;
}
d[len+1]=0;
for(i=len;i>0;i--)
{
for(j=0;j<d[i];j++)
{
if(d[i+1]!=6||j!=2)
ans+=dp[i][j];
}
if(d[i]==4||(d[i]==2&&d[i+1]==6))
break;
}
return ans;
}
int main()
{
int n,m;
init();
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)
break;
int sum=cal(m+1)-cal(n);
printf("%d\n",sum);
}
return 0;
}