P2602 [ZJOI2010] 数字计数&HDU 2089 (数位dp)

发布时间 2023-09-28 09:17:47作者: Simex

luogu
HDU
最近在复习数位dp
数位dp,就是在一些计数问题的时候按照一位一位的顺序依次计算,通常可以采用记忆化搜索的方式
这两道题就是很典型的数位dp
数位dp通常要记录是不是顶着上限,有没有前导零,到了哪一位以及一些特殊的条件要求。
数位dp通常要把某个区间的问题转变成两个区间的差来方便求解
比如说第一题,这一道题每一个数位填了几对后面数位除了前导零和是否顶着上限以外没有额外的影响,所以我们在求解的时候可以选择
直接在当前位数算出当前位的贡献然后去计算下一位的贡献。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<ctime>
#include<bitset>
#define ll long long
using namespace std;
ll f[15];
ll now[15];
ll len;
ll ksm[15];
ll p[15];
ll a,b;
ll dfs(int len,int x,bool zero,bool lim){
	if(!len){
		return 0;
	}
	if(!lim&& !zero && (~f[len])) return f[len];
	ll cnt=0;
	int up= lim? p[len] : 9;
	for(int i=0;i<=up;++i){
		if(i==0&&zero) cnt+=dfs(len-1,x,1,lim&&i==up);
		else if (i==x&&lim&&i==up){
			cnt+=1+now[len-1]+dfs(len-1,x,0,1);
		}else if(i==x){
			cnt+=ksm[len-1]+dfs(len-1,x,0,0);
		}else{
			cnt+=dfs(len-1,x,0,lim&&i==up);
		}
	}
	if((!lim)&&(!zero)) f[len]=cnt;
	return cnt;
}
ll ask(ll x,ll tag){
	ll len=0;
	while(x){
		p[++len]=x%10;
		x/=10;
		now[len]=now[len-1]+ksm[len-1]*p[len];
	}
	memset(f,-1,sizeof(f));
	return dfs(len,tag,1,1);
}
int main(){
	scanf("%lld%lld",&a,&b);
	ksm[0]=1;
	for(int i=1;i<=12;++i){
		ksm[i]=ksm[i-1]*10ll;
	}
	for(int i=0;i<=9;++i) printf("%lld ",ask(b,i)-ask(a-1,i));
	return 0;
}

再看HDU这一道题,大体的思路是相似的,但是知道当前位是多少,虽然说后面的答案是固定的,也就是说我们可以记忆化处理,但是
仅仅知道当前位是不可能知道后面的情况的,必须往后搜索才可以。所以说计算贡献的位置有所变化。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<ctime>
#include<bitset>
#define ll long long
using namespace std;
ll f[15];
ll now[15];
ll len;
ll ksm[15];
ll p[15];
ll a,b;
ll dfs(int len,int x,bool zero,bool lim){
	if(!len){
		return 0;
	}
	if(!lim&& !zero && (~f[len])) return f[len];
	ll cnt=0;
	int up= lim? p[len] : 9;
	for(int i=0;i<=up;++i){
		if(i==0&&zero) cnt+=dfs(len-1,x,1,lim&&i==up);
		else if (i==x&&lim&&i==up){
			cnt+=1+now[len-1]+dfs(len-1,x,0,1);
		}else if(i==x){
			cnt+=ksm[len-1]+dfs(len-1,x,0,0);
		}else{
			cnt+=dfs(len-1,x,0,lim&&i==up);
		}
	}
	if((!lim)&&(!zero)) f[len]=cnt;
	return cnt;
}
ll ask(ll x,ll tag){
	ll len=0;
	while(x){
		p[++len]=x%10;
		x/=10;
		now[len]=now[len-1]+ksm[len-1]*p[len];
	}
	memset(f,-1,sizeof(f));
	return dfs(len,tag,1,1);
}
int main(){
	scanf("%lld%lld",&a,&b);
	ksm[0]=1;
	for(int i=1;i<=12;++i){
		ksm[i]=ksm[i-1]*10ll;
	}
	for(int i=0;i<=9;++i) printf("%lld ",ask(b,i)-ask(a-1,i));
	return 0;
}