题意:给定一个区间[a,b],求不含数字4和子串62的数的个数。(0<a<b<1e6)
数位DP入门题,虽然可以纯模拟过掉,不过最好趁这个机会理一下数位DP的思想。
首先说它是DP,倒不如说是深搜套一个记忆化的壳。在深搜之前,需要把数拆成数组,然后我们记录取到第k位,要取之后的n-k个数的方案数(不考虑是否超过范围),以及取到这一位的状态(视题意而定)。一般在深搜的过程中,要传下下面几个参数:k、status、ismax,分别代表取到第几位,目前的状态,前面取的位是否等于原数的对应位(等于则这一位不能大于原数的这一位)。
对于这道题只需将status定为上一位是否为6,如果status为true,则这一位不能为2。
模拟
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define FOR(i,x,y) for(int i=(x);i<=(y);i++) #define DOR(i,x,y) for(int i=(x);i>=(y);i--) using namespace std; int dp[1000003]; //将不符合的数累计 int mark[10]; void dfs(int k,int sum) { if(k>6) { dp[sum]=1; return; } if(mark[k])dfs(k+1,sum*10+mark[k]); else FOR(i,0,9)dfs(k+1,sum*10+i); } void init() { FOR(i,1,6) { memset(mark,0,sizeof(mark)); mark[i]=4; dfs(1,0); } FOR(i,1,5) { memset(mark,0,sizeof(mark)); mark[i]=6,mark[i+1]=2; dfs(1,0); } FOR(i,1,1e6)dp[i]+=dp[i-1]; } int main() { init(); int x,y; while(scanf("%d%d",&x,&y)&&(x||y)) printf("%d\n",y-x+1-(dp[y]-dp[x-1])); return 0; }
数位DP
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define FOR(i,x,y) for(int i=(x);i<=(y);i++) #define DOR(i,x,y) for(int i=(x);i>=(y);i--) using namespace std; int dp[10][2]; int num[10]; //搜到第k位,状态为status,是否前面的值都取到最大 int dfs(int k,bool status,bool ismax) { if(k==0)return 1; //对!ismax的情况保存 if(!ismax && dp[k][status]!=-1)return dp[k][status]; int maxer=ismax?num[k]:9,res=0; FOR(i,0,maxer) if(i!=4 && !(status&&i==2)) res+=dfs(k-1,i==6,ismax&&i==maxer); if(!ismax)dp[k][status]=res; return res; } int solve(int k) { int p=0; while(k) //按位拆开 { num[++p]=k%10; k/=10; } return dfs(p,0,1); } int main() { int A,B; //有些题dp会经常是0,避免重复算到 memset(dp,-1,sizeof(dp)); while(scanf("%d%d",&A,&B)&&(A||B)) printf("%d\n",solve(B)-solve(A-1)); return 0; }