P2602 [ZJOI2010] 數字計數&HDU 2089 (數位dp)

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章