poj 2411 骨牌覆蓋問題 狀壓dp
阿新 • • 發佈:2019-01-28
題意:用1*2 的矩形通過組合拼成大矩形,求拼成指定的大矩形有幾種拼法
分析:
這題的關鍵在於什麼狀態是合法的,可以這樣想,用1表示有骨牌覆蓋,用0表示空著。在覆蓋第i行的時候,那麼如果覆蓋的狀態是合法的,覆蓋完後第i-1行必須沒有空格了(全是1111),所以可以看出,每一行的狀態必須要有偶數個1相連,為什麼?
由於在做第i行dp時必須完全覆蓋第i-1行,只要抓住這個條件不放就行。
1、如果第i行中有0,則第i-1行一定為1;
2、如果第i行中為1的x列第i-1行為0,說明第i行肯定是豎著放的;
3、如果第i行中為1的x列第i-1行的該列也為1,可能性只有一個,第i行是橫放的,所以第i行的x+1列也必須為1,又因為第i行的x+1列為1是因為橫著放的,所以第i-1行的x+1列也必須為1。
【狀態表示】dp[state][i]第i行狀態為state時候的方案數
【轉移方程】dp[state][i] += dp[state'][i-1] state'為i-1行的,不與i行狀態state衝突的狀態
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; typedef long long ll; ll f[1<<11][11]; bool ok[1<<11]; int n,m; inline bool check(int x) { int bit=0; while(x){ if(x&1)bit++; else{ if(bit&1)return false; bit=0; } x>>=1; } if(bit&1)return false; return true; } inline bool judge(int a,int b,int S) { if((a|b)!=S-1)return false; return ok[a&b]; } void init() { memset(ok,0,sizeof(ok)); for(int s=0;s<(1<<11);s++){ if(check(s))ok[s]=1; } } int main() { init(); //freopen("f.txt","r",stdin); int S; while(~scanf("%d%d",&n,&m)&&(n+m)){ if(m>n){ int t=m;m=n;n=t; } S=1<<m; memset(f,0,sizeof(f)); for(int s=0;s<S;s++){ if(ok[s])f[s][0]=1; } for(int i=1;i<n;i++){ for(int s=0;s<S;s++){ for(int ss=0;ss<S;ss++){ if(judge(s,ss,S)){ f[s][i]+=f[ss][i-1]; } } } } printf("%lld\n",f[S-1][n-1]); } return 0; }
還看到用人用dfs寫的,在轉移的時候逐行判斷,好像更快點。