首页 > 其他 > 详细

POJ 2411 Mondriaan's Dream (状压DP)

时间:2014-02-14 22:59:47      阅读:306      评论:0      收藏:0      [点我收藏+]

dp[i][j]表示第i行  为j 状态的有多少种方法。

1表示这一列的格子是自己放的,是竖的第一行或横的.如果是0则表示这一个格子是竖的第二格的


就要写一个匹配的函数,保证上一行可以跟这一行凑出来合法的满满的两行。


也就是其他博客里说的。


1、如果第i行中有0,则第i-1行一定为1;

2、如果第i行中为1的x列第i-1行为0,说明第i行肯定是竖着放的;

3、如果第i行中为1的x列第i-1行的该列也为1,可能性只有一个,第i行是横放的,所以第i行的x+1列也必须为1,又因为第i行的x+1列为1是因为横着放的,所以第i-1行的x+1列也必须为1。


那么状态转移就很简单了。

dp[i][cur] += dp[i-1][last]  如果last和cur能匹配的话。


还有输出的时候为什么是输出dp[n][1<<m-1]   。

因为你在这一行是1 上一行是0    等同于这一行是0 上一行是 1

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
typedef long long LL;
using namespace std;

LL dp[11][1<<11];
int n,m;

bool ok(int x)
{
    int pos=1;
    int count=0;
    for(int i=0;i<(1<<m);i++,pos<<=1)
    {
        if(x&pos)count++;
        else
        {
            if(count&1)return false;
            else count=0;
        }
    }
    return !(count&1);
}
bool match(int last,int cur)
{
    for(int pos=1;pos<(1<<m);)
    {
        if(!(last&pos) && !(cur&pos))return false;
        else if((last&pos)&&(cur&pos))
        {
            if(((pos<<1)&last) && ((pos<<1)&cur))pos<<=2;
            else return false;
        }
        else pos<<=1;
    }
    return true;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF && n && m)
    {
        if(n<m)swap(n,m);

        memset(dp,0,sizeof dp);

        for(int i=0;i<(1<<m);i++)
        {
            if(ok(i))dp[0][i]=1;
        }

        for(int i=1;i<n;i++)
        {
            for(int last=0;last<1<<m;last++)
            {
                for(int cur=0;cur<1<<m;cur++)
                if(match(last,cur))
                dp[i][cur]+=dp[i-1][last];
            }
        }
        cout<<dp[n-1][(1<<m)-1]<<endl;
    }
    return 0;
}


POJ 2411 Mondriaan's Dream (状压DP)

原文:http://blog.csdn.net/u010709592/article/details/19178021

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!