您的位置:首页 > 其它

POJ2411 Mondriaan's Dream(状态压缩DP)

2015-11-16 22:14 330 查看
人生第一道状态压缩DP,我这个弱渣想了N天终于弄懂了◑﹏◐

题目大意:求1*2的地板填满n*m的砖块有多少种不同的方法

思路:位运算+DFS+状态压缩。

当高度和宽度都为奇数时答案为0。

对于每一个位置,我们有三种放置方法:

1. 竖直放置

2. 水平放置

3. 不放置

d为当前列号 ,初始化d, now, pre都为0;对应以上三种放置方法,now, pre的调整为:

1. d = d + 1, now << 1 | 1, pre << 1;

2. d = d + 2, now << 2 | 3, pre << 2 | 3;

3. d = d + 1, now << 1, pre << 1 | 1;

先就第一种情况解释一下,另外的两种情况可以类推

now<<1|1即为把s1的二进制表示后面加上一个1,对就于本题来说就是(d+1)列上放

置,pre<<1即为把s2的二进制表示后面加上一个0,对于本题来说就是(d+1)列上不放置。

now对应于本行的状态,pre对应于上一行的状态,能竖直放置意味着上一行的(d+1)列是空着的,因此此时上一行的状态为pre<<1,同时竖

置放置了之后,则本行(d+1)行放置了东西,状态于是变为now<<1|1;

当d = w时保存状态

对于初始时的f值,可以假设第0行全满,第一行只有两种放法:

1. 水平放置 d = d + 2, s << 2 | 3;

2. 不放置 d = d + 1, s << 1;

最后只要判断h行是否全为1就行了,只取最后一行全为1的答案,也就是(1<

#include <iostream>
#include <string>
#include <cstring>
#include <stdio.h>
#include <cstdlib>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <functional>
#include <algorithm>
#include <cmath>
#include <assert.h>
#include <stack>
using namespace std;
#define maxn 1<<12
int h,w,p,q;
long long dp[2][maxn];

void dfs(int col,int now,int pre) //now当前行状态,pre前一行状态
{
if(col==w)  //col已经移到当前行最左边一列
{
dp[q][now]+=dp[p][pre];
return;
}
if(col+1<=w)
{
dfs(col+1,now<<1,pre<<1|1);   //不放 ,故而pre<<1|1,即将当前行的下一列由0变1
//当前行在下一个dfs变成前一行,即pre
dfs(col+1,now<<1|1,pre<<1);    //竖放
//当下一行行的下一列由0变1(now<<1|1)
}
if(col+2<=w)
dfs(col+2,now<<2|3,pre<<2|3);  //横放
//3二进制为100,或运算将当前行的下一行同一列由0变成1
}

void solve()
{
int i;
if((( w * h ) & 1) )  //&1相当于%2,但是与运算更快
{
printf("0\n");
return;
}
memset(dp,0,sizeof(dp));
p=0;
dp[0][(1<<w)-1]=1;
for(i=1;i<=h;i++)
{
q=p^1;    //滚动数组,p,q轮替交换,p是上一行,q是当前行
dfs(0,0,0);
memset(dp[p],0,sizeof(dp[0]));
p=q;
}
printf("%lld\n",dp[q][(1<<w)-1]);
}

int main()
{
while(~scanf("%d%d",&h,&w)&&(w||h))
{
if(w>h)swap(w,h);
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  压缩 poj dp