1.poj2411
题目大意:用2*1的骨牌覆盖满一个n*m的矩阵,求方案数。(n,m<=11)
题目分析:由于n和m都很小,可以想到状态压缩dp。如果我们f[i][j]表示某i行的状态j,在状态j中,1表示已经覆盖,0表示没有覆盖,那么有三种情况:1.不放 2.横着放 3.竖着放。用dfs来寻找每行和它上行之间可以转换的状态,上行状态是s1,这行状态是s2,l是列号,那么dfs的扩展方式如下:
1.l=l+1,s1=(s1<<1)+1,s2=(s2<<1)//因为如果这一行的这一个地方不放骨牌,那么上一行如果空着,就不符合放满骨牌的规则了。
2.l=l+2,s1=(s1<<2)+3,s2=(s2<<3)+3.
3.l=l+1,s1=(s1<<1),s2=(s2<<1)+1//因为如果这一行要竖着放,那么上一行此处必须空着
初始条件:f[0][1...111]=1//第一行不能竖着放
代码:
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<climits>
using namespace std;
const int INF=(1<<11);
int n,m,maxn,sum=0;//sum:对应关系的数量
int gx1[1000001],gx2[1000001];
long long f[12][INF];
void dfs(int l,int now,int last){
if(l>m)return;
if(l==m){sum++;gx1[sum]=now;gx2[sum]=last;return;}
dfs(l+1,(now<<1),(last<<1)+1);//不放
dfs(l+1,(now<<1)+1,(last<<1));//放竖着
dfs(l+2,(now<<2)+3,(last<<2)+3);//放横着
}
int main()
{
int i,j,k,kl,tmp1,tmp2,head,tail;
while(1){
scanf("%d%d",&n,&m);
if(n==0&&m==0)break;
sum=0;
memset(f,0,sizeof(f));
if(n%2==1&&m%2==1){printf("0\n");continue;}
if(n>m){kl=n;n=m;m=kl;}
maxn=(1<<m)-1;
f[0][maxn]=1;//第1行不能放竖的
dfs(0,0,0);
for(i=1;i<=n;i++)
for(j=1;j<=sum;j++)f[i][gx1[j]]+=f[i-1][gx2[j]];
printf("%lld\n",f[n][maxn]);
}
return 0;
}
2.SGU131
题目大意:用2×1的骨牌或者2×2挖去一块的“L”形骨牌覆盖整个棋盘,求方案数
题目分析:见表格,b1表示上一行对这一行的影响,1表示影响了0表示没有,s1表示上一行状态,s2表示这一行状态。
如果这一行要放竖着的骨牌,那么上一行此处不能放骨牌。如果上一行此处没有影响,就必须要放了骨牌才能覆盖满。
编号 | 状态 | 条件 | 转移s1,s2 | 转移b1,b2 |
1 | 0 0 0 0 | 无 | s1=(s1<<1)+1-b1 s2=(s2<<1)+b2 | b1=0 b2=0 |
2 | 0 0 1 1 | b2=0 | s1=(s1<<1)+1-b1 s2=(s2<<1) | b1=0 b2=1 |
3 | 1 0 1 0 | b1=0 b2=0 | s1=(s1<<1) s2=(s2<<1)+1 | b1=0 b2=0 |
4 | 1 0 1 1 | b1=0 b2=0 | s1=(s1<<1) s2=(s2<<1)+1 | b1=0 b2=1 |
5 | 1 1 0 1 | b1=0 | s1=(s1<<1) s2=(s2<<1)+b2 | b1=1 b2=1 |
6 | 1 1 1 0 | b1=0 b2=0 | s1=(s1<<1) s2=(s2<<1)+1 | b1=1 b2=0 |
7 | 0 1 1 1 | b2=0 | s1=(s1<<1)+1-b1 s2=(s2<<1)+1 | b1=1 b2=1 |
代码:
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<climits>
using namespace std;
const int INF=(1<<9);
long long f[10][INF];
int n,m,x;
void search(int l,int s1,int s2,int b1,int b2){
if(l>m)return;
if(l==m){
if(b1==0&&b2==0){f[x][s2]+=f[x-1][s1];}
return;}
search(l+1,(s1<<1)+1-b1,(s2<<1)+b2,0,0);//1
if(b1==0&&b2==0){
search(l+1,(s1<<1),(s2<<1)+1,0,0);//3
search(l+1,(s1<<1),(s2<<1)+1,0,1);//4
search(l+1,(s1<<1),(s2<<1)+1,1,0);//6
}
if(b1==0){
search(l+1,(s1<<1),(s2<<1)+b2,1,1);//5
}
if(b2==0){
search(l+1,(s1<<1)+1-b1,(s2<<1)+1,0,1);//2
search(l+1,(s1<<1)+1-b1,(s2<<1)+1,1,1);//7
}
}
int main()
{
int i,j,tmp,maxn;
scanf("%d%d",&n,&m);
if(n<m){tmp=n;n=m;m=tmp;}//减少递归层数
maxn=(1<<m)-1;
f[0][maxn]=1;
for(x=1;x<=n;x++)//防止爆空间,不需要预存所有方案
search(0,0,0,0,0);
printf("%lld",f[n][maxn]);
return 0;
}
/*
1: 0 0
0 0
2: 0 0
1 1
3: 1 0
1 0
4: 1 0
1 1
5: 1 1
0 1
6: 1 1
1 0
7: 0 1
1 1
*/