A decorative fence 計數

【一句話題意】給n塊長度爲1~n的木板,組成一個長度爲n的序列,滿足相鄰的木板都比自己長或短。方案按字典序排列。有多組詢問,問當有ai塊木板時,第bi號方案是什麼。
ai<=20,bi<=263
【分析】類似於倍增dp的“拼湊”思想和手推康託展開時的方式,我們可以用“試填”的方式來確定第bi號方案中各個木板的長度。比如從小到大枚舉,如果當第一塊木板長爲h時,N-1塊木板的構成的方案數T大於bi,則第一塊木板長度就應該是h;否則就應該是更長的,同時從bi中減去減去T。

爲了加快“試填“的速度,我們要先預處理出所有的T值。這就需要把整個程序分成兩個部分,dp預處理和“試填”(對詢問的回答)。

dp部分定義f[i,j,k]表示用i塊長度不同木板構成左邊的序列,同時最左邊的木塊長度從小到大排爲第j位,並且狀態爲k(k=0表示處於低位,k=1表示處在高位)的方案總數。轉移方程爲
&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;f[i,j,0]=Σp=1i1f[i1,p,1]\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;f[i,j,0]=\Sigma^{i-1}_{p=1}f[i-1,p,1]
&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;f[i,j,1]=Σp=1i1f[i1,p,0]\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;f[i,j,1]=\Sigma^{i-1}_{p=1}f[i-1,p,0]
同樣的dp方式還可以用於離散化之後的問題,兩者是等價的。

再是“試填”部分。特別的第一塊木板既有可能是高位又有可能是低位,所以要特殊處理。其他的操作大致就是開頭所講。
【code】

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL n,m,T;
LL f[21][21][2];
void prework(){
	f[1][1][0]=f[1][1][1]=1;
	for(int i=2;i<=20;i++)
		for(int j=1;j<=i;j++){
			for(int p=j;p<i;p++)
				f[i][j][0]+=f[i-1][p][1];
			for(int p=1;p<j;p++)
				f[i][j][1]+=f[i-1][p][0];
		}
}
int main(){
	prework();
	cin>>T;
	while(T--){
		cin>>n>>m;
		bool used[21];
		memset(used,0,sizeof(used));
		int last,k;
		for(int j=1;j<=n;j++){
			if(f[n][j][1]>=m){
				last=j,k=1;
				break;
			}
			else m-=f[n][j][1];
			if(f[n][j][0]>=m){
				last=j,k=0;
				break;
			}
			else m-=f[n][j][0];
		}
		used[last]=1;
		printf("%d",last);
		for(int i=2;i<=n;i++){
			k^=1;
			int j=0;
			for(int len=1;len<=n;len++){
				if(used[len]) continue;
				j++;
				if(k==0&&len<last||k==1&&len>last){
					if(f[n-i+1][j][k]>=m){
						last=len;
						break;
					}
					else m-=f[n-i+1][j][k];
				}
			}
			used[last]=1;
			printf(" %d",last);
		}
		printf("\n");
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章