题意:给定一个区间[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;
}