一道十分经典的数位DP的题目。
dp[i][j]表示最高位是数字i,连同最高位在内共有j位。注意边界的初始化。
接下来就是区间划分,特殊情况处理.....对了,如果不知道自己的方法是否正确,可以写一个测试函数:
bool check(int n){ //判断n是否符合条件 int a[20]; int c=0; while(n>0){ a[c++]=n%10; n/=10; } for(int i=0;i<c;++i) if(a[i]==4) return false; for(int i=0;i<c-1;++i){ if(a[i]*10+a[i+1]==26) return false; } return true; } inline int solve(int n){ //可以求得0~n之间有多少符合条件的数 int ans=0; for(int i=0;i<=n;++i) if(check(i)) ++ans; return ans; }
AC代码:
#include<cstdio> #include<cstring> #include<cmath> const int maxn=20; int dp[10][maxn]; void deal(){ memset(dp,0,sizeof(dp)); //初始化边界 for(int i=0;i<10;++i){ if(i==4) continue; else dp[i][1]=1; } for(int i=2;i<10;++i){ //length for(int j=0;j<10;++j){ if(j==4) continue; for(int k=0;k<10;++k){ dp[j][i]+=dp[k][i-1]; } } dp[6][i]-=dp[2][i-1]; } } inline int solve(int pre,int n,int len){ if(pre==62||pre%10==4||pre/10==4) return 0; if(len==1){ int ans=0; for(int i=0;i<=n;++i) if(i==4) continue; else ++ans; if(pre%10==6&&n>=2) --ans; return ans; } int ans=0; int m=(int)pow(10,len-1); int up=n/m; for(int i=0;i<up;++i){ ans+=dp[i][len]; } if(pre%10==6&&up>2) ans-=dp[2][len]; return ans+solve(pre%10*10+up,n%m,len-1); } inline int getlen(int n){ if(n==0) return 1; int c=0; while(n>0){ n/=10; ++c; } return c; } int main(){ deal(); int l,r; while(scanf("%d%d",&l,&r)!=EOF&&l&&r){ int a=solve(0,r,getlen(r)); int b=solve(0,l-1,getlen(l-1)); //printf("%d-%d=%d\n",a,b,a-b); printf("%d\n",a-b); } return 0; }
如有不当之处欢迎指出!