POJ 2411 解题报告

传送门:http://poj.org/problem?id=2411

题目简述

有一个\(W\)\(H\)列的广场,需要用\(1*2\)小砖铺满,小砖之间互相不能重叠,问
有多少种不同的铺法?

输入数据:

只有一行\(2\)个整数,分别为\(W\)\(H\)\((1<=W,H<=11)\)

输出数据:

只有\(1\)个整数,为所有的铺法数。


妥妥状压

我们向上或者向右放砖头。

为什么呢?

\(dp[i][j]\)代表\(i\)\(j\)状态的方案数(\(j\)中的\(1\)代表这个位置在此行时是否被覆盖,不论它是被自己或者别人覆盖的)

此时当前行的\(0\)即可以约束下一行,向上放的过程。向右放即在本行处理。

处理的时候小心一点

code

#include <cstdio>
#include <cstring>
#define ll long long
const int N=12;
ll dp[N][1<<N];//dp[i][j]表示第i行j状态方案数
ll t[1<<N];
int m,n;
//其中,0代表在当前行时没有覆盖,1代表已经覆盖
//行数,上一行的状态,第几个位置,是否被左边照看,当前数字
void dfs(int line,int l_li,int dep,int is,int tt)
{
    if(dep==m+1)
    {
        dp[line][tt]+=dp[line-1][l_li];
        return;
    }
    int flag=!(l_li>>(m-dep)&1);//是否需要看上一行的
    if(is&&!flag)
        dfs(line,l_li,dep+1,0,tt<<1|1);
//被上一个看且不看上一行
    if(!is&&flag)
        dfs(line,l_li,dep+1,0,tt<<1|1);
//没被上一个看且要看上一行
    if(!is&&!flag)
    {
        if(dep!=m) dfs(line,l_li,dep+1,1,tt<<1|1);
        dfs(line,l_li,dep+1,0,tt<<1);
    }
//没被上一个看且不看上一行
}

int main()
{
    scanf("%d%d",&n,&m);
    while(m!=0&&n!=0)
    {
        memset(dp,0,sizeof(dp));
        dp[0][(1<<m)-1]=1;
        int cnt=0;
        t[++cnt]=(1<<m)-1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=cnt;j++)
                dfs(i,t[j],1,0,0);
            cnt=0;
            for(int j=0;j<(1<<m);j++)
                if(dp[i][j])
                    t[++cnt]=j;
        }
        printf("%lld\n",dp[n][(1<<m)-1]);
        scanf("%d%d",&n,&m);
    }
    return 0;
}

2018.5.10

猜你喜欢

转载自www.cnblogs.com/ppprseter/p/9020721.html