Mondriaan’s Dream(状压dp)
题目大意:用1×2的方块填满NxM的大矩形,问填满的方法有多少种。
解题思路:利用先填好竖着的方块,剩下的空格再用横着的来填,且要求填好竖着的方块时,每一行都要能用横着的方块填满(即连续的空出来的位置必须是偶数,即合法)
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#define ll long long
using namespace std;
const int maxn=12;
ll dp[maxn][1<<maxn];//后面的表示的是,这一行会向下一行突出的状态
bool ap[1<<maxn];
vector<int> st[1<<maxn];
int main(void)
{
int N,M;
while(~scanf("%d %d",&N,&M)&&(N||M))
{
if(N*M%2)//可以先排除一些,面积必须是偶数
{
puts("0");
continue;
}
for(int i=0;i<(1<<M);i++)//处理每一行该有的状态,就是合法的状态,每一行空出来的都必须是偶数
{
int sum=0;
for(int j=0;j<M;j++)
{
if(i&(1<<j))//遇到了没空的,检查空的是否为偶数
{
if(sum&1)
{
ap[i]=false;
break;
}
sum=0;
}else sum++;
}
if(sum&1)ap[i]=false;//要看看剩下来空的是否为偶数
else ap[i]=true;
}
for(int j=0;j<(1<<M);j++)//预处理,由于题目有多组测试样例,可能会超时,所以要预处理
{//每一行突出的状态可以更新出什么状态
st[j].clear();//注意多组测试样例
for(int k=0;k<(1<<M);k++)
{
if((j&k)||!ap[j|k])continue;
st[j].push_back(k);
}
}
memset(dp,0,sizeof(dp));//注意多组测试样例
dp[0][0]=1;
for(int i=1;i<=N;i++)
for(int j=0;j<(1<<M);j++)
for(int k=0;k<st[j].size();k++)
dp[i][j]+=dp[i-1][st[j][k]];
cout<<dp[N][0]<<endl;//最后一行向后面一行突出的要为0
}
return 0;
}
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/288861.html