HDU-2089 不要62(模拟/数位DP)

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

猜你喜欢

转载自blog.csdn.net/paulliant/article/details/80329092