状压dp之棋盘覆盖

来源:互联网 发布:人工智能疾病诊断 编辑:程序博客网 时间:2024/06/09 23:56

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,b210 0
0 0无s1=(s1<<1)+1-b1
s2=(s2<<1)+b2b1=0
b2=020 0
1 1b2=0s1=(s1<<1)+1-b1
s2=(s2<<1)b1=0
b2=131 0
1 0b1=0
b2=0s1=(s1<<1)
s2=(s2<<1)+1b1=0
b2=041 0
1 1b1=0
b2=0s1=(s1<<1)
s2=(s2<<1)+1b1=0
b2=151 1
0 1b1=0s1=(s1<<1)
s2=(s2<<1)+b2b1=1
b2=161 1
1 0b1=0
b2=0s1=(s1<<1)
s2=(s2<<1)+1b1=1
b2=070 1
1 1b2=0s1=(s1<<1)+1-b1
s2=(s2<<1)+1b1=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);//1if(b1==0&&b2==0){search(l+1,(s1<<1),(s2<<1)+1,0,0);//3search(l+1,(s1<<1),(s2<<1)+1,0,1);//4search(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);//2search(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*/


0 0
原创粉丝点击