poj 2411 Mondriaan's Dream 状态压缩dp

 邓姐推荐做这道题,果然是一道不错的题目,据说是插头dp,不过我不会哦,咋办呢,这种题只好暴力乱搞了

一开始用3进制来存储状态 0代表横放 1代表竖着放朝上 2代表竖着放朝下 然后暴力搜了一下 复杂度3^22 果断超时了

后来想了一下,其实上一行的每个格子状态都搜完了之后,只要不是朝下 那么就对下一行没有影响了 于是更改了一下状态存储方式

0代表这个格子放的方式不会影响下一行 1代表会影响下一行

容易证明 不冲突的两行 下一行有且只有一种合法的方式

所以复杂度就从 3^22缩减到了2^22  并且二进制存储与位运算 速度飞快 就过了

具体的就看代码咯

View Code
Source Code

Problem: 2411        User: tangrui
Memory: 416K        Time: 47MS
Language: C++        Result: Accepted
Source Code
#include<iostream>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
long long t[(1<<11)+10][11];
int x;
// 0代表仅占自己这行 1代表竖着放占了下一行
int selfcheck(int a)
{
    int odd=0;         //相邻的两行同样位置都是0代表这个位置是横着放的 因此必须是偶数个
    for(int k=a,i=0;i<n;i++)
    {
        if(!(k&1))odd=!odd;
        else if(odd) return 0;
        k>>=1;
    }
    if(odd) return 0;
    return 1;
}
int correct(int a,int b)
{
    if(a&b)return 0;          //当相邻的两行有同样位置的两个1时一定是冲突的
    return selfcheck(a|b);  
}
long long dp(int now,int k)
{

    if(k==m-1)
    {
        return correct(0,now); //因为最后一行状态只能是0 所以倒数第二行就判断
    }   
    if(t[now][k])return t[now][k]; //你懂的
    for(int i=0;i<x;i++)
    {
        if(correct(i, now))
            t[now][k]+=dp(i,k+1);
    }
    return t[now][k];
}
int main()
{
    while(cin>>n>>m&&(n|m))
    {
        if(n==0||m==0||n*m%2==1)
        {
            cout<<0<<endl;
            continue;
        }
        memset(t,0,sizeof(t));
        if(n>m) swap(n,m);  //为了时间 尽量让n小些
         x=1<<n;            //每一行的总状态数为 2^n
        cout<<dp(0,0)<<endl;
    }
}

 

posted @ 2012-12-05 11:47  goagain  阅读(764)  评论(0编辑  收藏  举报