题目:点击打开链接
题意:用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写的,在转移的时候逐行判断,好像更快点。