hdu_5555_Immortality of Frog(狀壓DP)

題目連接:hdu_5555_Immortality of Frog

題意:

給你一個NxN的網格,第N行的每一列都有個青蛙,這些青蛙只會往上走,上帝會在每個膜中放一個長生不老的藥,一共有N個膜,每個膜覆蓋一些區間,如果這個區間恰好爲N那麼就是好膜,否則是壞膜,每個青蛙最多隻能穿過10個壞膜,問全部青蛙吃到藥,並全部到頂層的分配方案。

題解:

1.我們首先統計每一列有多少個壞膜,其中一列如果大於10,那麼青蛙肯定不能全部到達頂部,ans=0;

2.假設青蛙把全部的壞膜吃完了,當前的方案數爲p,好膜是都可以吃的,那麼此時的答案就是好膜的個數的階乘*p。

3.這時我們就該來算全部吃完壞膜的方案數了。

4.首先每一列最多隻有10個壞膜,那麼我們可以用狀態壓縮來保存每一列壞膜的狀態,但這個狀態只是這一列的相對位置,比如這一列第10行的壞膜的相對位置爲1,第24行的壞膜相對位置爲2

5.我們dp[i][j]表示第i列的壞膜相對位置的吃掉情況,那麼我們要轉移到i+1列,就要轉移第i列已經吃過的壞膜的情況到第i+1列,因爲j表示的是當前列的壞膜相對位置,我們要對應找到i+1列的壞膜的相對位置,列如:第i列有 第12,15,18,20是壞膜,第i+1列有第15,20,30,40是壞膜,假設第i列的第15行壞膜已經吃掉,第15行在第i列的相對位置爲2,此時我們要轉移到i+1列上,對應的就是第i+1列的15行,第15行在i+1列的相對位置爲1,這樣就是dp[i][1<<(2-1)]轉移到了dp[i+1][1<<(1-1)]。

6.到最後我們取的是最後一列的全部壞膜吃掉的情況,這裏就包含了所有壞膜吃完的情況,然後乘上好膜的階乘即可


#include<cstdio>
#include<vector>
#define F(i,a,b) for(int i=a;i<=b;i++)
typedef long long LL;
using namespace std;

const int N=1030,mod=105225319;
int dp[N][N],n,l[N],r[N],good,jie[N],p1[20],p2[20];
vector<int>g[N];

void init(){
	jie[0]=1;
	F(i,1,1000)jie[i]=(LL)jie[i-1]*i%mod;
}

void del(int x){
	F(i,0,(int)g[x].size()-1){
		p1[i]=-1;
		F(j,0,(int)g[x+1].size()-1)
			if(g[x][i]==g[x+1][j]){p1[i]=j;break;}
	}
	F(i,0,(int)g[x+1].size()-1){
		p2[i]=-1;
		F(j,0,(int)g[x].size()-1)
			if(g[x+1][i]==g[x][j]){p2[i]=j;break;}
	}
}

inline int new_s(int x,int y){
	int ans=0;
	F(i,0,(int)g[x].size()-1){
		if(p1[i]==-1){if(!((y>>i)&1))return -1;}
		else if(y>>i&1)ans|=(1<<p1[i]);
	}//這個壞膜在當前列的編號對應下一列的編號
	return ans;
}

inline void up(int &x,int y){x+=y,x=x>mod?x-mod:x;}

int main(){
	init();
	int t;scanf("%d",&t);
	F(ic,1,t){
		scanf("%d",&n),good=0;
		F(i,1,n)scanf("%d",l+i),g[i].clear();
		F(i,1,n)scanf("%d",r+i);
		F(i,1,n)if(l[i]==1&&r[i]==n)good++;
		else F(j,l[i],r[i])g[j].push_back(i);
		int flag=0,ans=0;//壞膜大於10,無法分配
		F(i,1,n)if(g[i].size()>10){flag=1;break;}
		if(!flag){
			F(i,1,n){//dp初始化
				int sz=g[i].size();
				F(j,0,(1<<sz))dp[i][j]=0;
			}
			dp[0][0]=1;
			F(i,0,n-1){
				del(i);
				F(j,0,(1<<(int)g[i].size())-1){
					int now=new_s(i,j);
					if(now!=-1){//將上一列已經吃過的壞膜轉移到這列對應的狀態
						up(dp[i+1][now],dp[i][j]);
						F(k,0,(int)g[i+1].size()-1)//如果上一列沒有這個壞膜或者有但沒吃,那麼這一列肯定吃掉這個膜
							if(p2[k]==-1||!(now>>k&1))
							up(dp[i+1][now|(1<<k)],dp[i][j]);
					}
				}
			}
		ans=(LL)jie[good]*dp[n][(1<<(int)g[n].size())-1]%mod;
		}
		printf("Case #%d: %d\n",ic,ans);
	}
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章