poj2411(状压dp)

这题不看题解,根本没思路。

思路如下:

对于一个矩形有3种方法:横放,竖放,不放。由于第i行只跟第i-1行的放置有关系,因为我们必须保证第i-1行使放满的,现在用dp[i][state]表示第i行

状态为state的方法,那么dp[i][now]=sum{dp[i-1][pre]}.

1 横放

如果第i行第d列我们选择横放,那么第i行的第d列及d+1列都是1了,第i-1行第d列及d+1列也都必须为1(保证是满的),及状态转移为:

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

2竖放

第i行第d列我们选择竖放,那么第i行第d列为1,第i-1行d列必须是0,(因为我们是竖着放的,如果前一行不是空的如何能放下呢),状态转移:

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

3不妨

第i行第d列不妨,那么第i-1行d列肯定是1,(保证是满的),状态转移:

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

这题用递推表示真心不懂,于是记忆优化搜索一下。

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
typedef __int64 lld;
#define oo 0x3f3f3f3f
#define Mod 1000000007
#define maxn (1<<11)+1
lld dp[maxn][12];
int n,m;

void dfs(int r,int c,int now,int pre)
{
    if(c==m)
    {
        dp[now][r]+=dp[pre][r-1];
        return ;
    }
    //竖放
    dfs(r,c+1,now<<1|1,pre<<1);
    //横放
    if(c+2<=m)
        dfs(r,c+2,now<<2|3,pre<<2|3);
    //不放
    dfs(r,c+1,now<<1,pre<<1|1);
}

int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        if(n==0&&m==0) break;
        if(n*m&1)
        {
            puts("0");
            continue;
        }
        int all=(1<<m)-1;
        memset(dp,0,sizeof dp);
        dp[all][0]=1;//0行堆满便于处理
        for(int i=1;i<=n;i++)
            dfs(i,0,0,0);
        printf("%I64d\n",dp[all][n]);//最后一行堆满了就是结果了
    }
    return 0;
}





















你可能感兴趣的:(dp,poj)