POJ 2411 Mondriaan's Dream 解题报告
传送门:http://poj.org/problem?id=2411
Mondriaan's Dream
题目简述
有一个\(W\)行\(H\)列的广场,需要用\(1*2\)小砖铺满,小砖之间互相不能重叠,问
有多少种不同的铺法?
输入数据:
只有一行\(2\)个整数,分别为\(W\)和\(H\),\((1<=W,H<=11)\)
输出数据:
只有\(1\)个整数,为所有的铺法数。
妥妥状压
我们向上或者向右放砖头。
为什么呢?
令\(dp[i][j]\)代表\(i\)行\(j\)状态的方案数(\(j\)中的\(1\)代表这个位置在此行时是否被覆盖,不论它是被自己或者别人覆盖的)
此时当前行的\(0\)即可以约束下一行,向上放的过程。向右放即在本行处理。
处理的时候小心一点
code
#include <cstdio>
#include <cstring>
#define ll long long
const int N=12;
ll dp[N][1<<N];//dp[i][j]表示第i行j状态方案数
ll t[1<<N];
int m,n;
//其中,0代表在当前行时没有覆盖,1代表已经覆盖
//行数,上一行的状态,第几个位置,是否被左边照看,当前数字
void dfs(int line,int l_li,int dep,int is,int tt)
{
if(dep==m+1)
{
dp[line][tt]+=dp[line-1][l_li];
return;
}
int flag=!(l_li>>(m-dep)&1);//是否需要看上一行的
if(is&&!flag)
dfs(line,l_li,dep+1,0,tt<<1|1);
//被上一个看且不看上一行
if(!is&&flag)
dfs(line,l_li,dep+1,0,tt<<1|1);
//没被上一个看且要看上一行
if(!is&&!flag)
{
if(dep!=m) dfs(line,l_li,dep+1,1,tt<<1|1);
dfs(line,l_li,dep+1,0,tt<<1);
}
//没被上一个看且不看上一行
}
int main()
{
scanf("%d%d",&n,&m);
while(m!=0&&n!=0)
{
memset(dp,0,sizeof(dp));
dp[0][(1<<m)-1]=1;
int cnt=0;
t[++cnt]=(1<<m)-1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=cnt;j++)
dfs(i,t[j],1,0,0);
cnt=0;
for(int j=0;j<(1<<m);j++)
if(dp[i][j])
t[++cnt]=j;
}
printf("%lld\n",dp[n][(1<<m)-1]);
scanf("%d%d",&n,&m);
}
return 0;
}
2018.5.10