題目:點擊打開鏈接
題意:用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寫的,在轉移的時候逐行判斷,好像更快點。