状态压缩-覆盖问题-poj 2411 2663


题目大意
用1*2的骨牌覆盖n*m的棋盘,问有多少种方案。可以旋转90°放。不能覆盖则输出0。


解题思路
用 f【i,s】表示 把前 i-1 行覆盖满、第 i 行覆盖状态为 s 的覆盖方案数。因为在第 i 行上放置的骨牌多也只能影响到第 i-1 行(竖着放的时候是占用当前行和上一行), 因此要满足在填第i行时,第1~i-2行已经全部填满,所以 DFS一行的状态,要使得填完(不一定填满)第i行时,第i-1行被填满。则容易得递推式:
 f[i][s] += f[i-1][s2]      f[0][1<<m-1]=1;
然后就是处理如何dfs的问题。参数:列p,当前行的状态now,上一行的状态last。那么有三种操作
①竖放:dfs(p+1,(now<<1)|1,last<<1) ; //竖放(占据当前行和上一行),在当前行竖放需要上一行的p+1列是空的
②横放:dfs(p+2,(now<<2)|3,(last<<2)|3) ; //横放:上一行当前列没有空位,没办法竖放
③不放:dfs(p+1,(now<<1),(last<<1)|1); //不放 : 前一行的p+1列没有空位,就不能放
(注*  以①为例,表示当前行在p+1列的时候,状态为(now<<1)|1,上一行状态为last<<1,与之前不同的是这次是从左往右找列,也就是走到下一列就让前面的向左移,腾出一个未定义的列使用)

代码
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int n,m,row;
long long f[12][1<<12];//这里要开longlong

void dfs(int p,int now,int last)
{
    if(p>m) return ;
    if(p==m)//到达最后一列了
    {
        f[row][now] += f[row-1][last];
    }
    dfs(p+1,(now<<1),(last<<1)|1); //不放 : 前一行的p+1列没有空位,就不能放
    dfs(p+1,(now<<1)|1,last<<1);//竖放(占据当前行和上一行),在当前行竖放需要上一行的p+1列是空的
    dfs(p+2,(now<<2)|3,(last<<2)|3);//横放:上一行当前列没有空位,没办法竖放
}
int main()
{
    while(scanf("%d%d",&n,&m))//n行m列
    {
        if(n==0 && m==0) break;
        if(n%2 && m%2)
        {
            printf("0\n");
            continue;
        }
        if(n<m) swap(n,m);//让小的当列

        memset(f,0,sizeof(f));

        f[0][(1<<m)-1] = 1; //第0行全放满是一种方案

        for(row=1;row<=n;++row)
            dfs(0,0,0);

      cout<<f[n][(1<<m)-1]<<endl;
    }
    return 0;
}




题目大意
用1*2的骨牌覆盖3*n的棋盘,问有多少种方案

解题思路
这道题和上一道题没什么太大区别,就是把m变成3,还是和好写的

代码
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int n;//n行3列
int f[35][1<<3];
int row;

void dfs(int p,int now,int last)
{
    if(p>3) return ;
    if(p==3)
    {
        f[row][now] +=f[row-1][last];
    }
    else
    {
        dfs(p+1,now<<1,(last<<1)|1);//不放
        dfs(p+2,(now<<2)|3,(last<<2)|3);//横放
        dfs(p+1,(now<<1)|1,last<<1);
    }

}

int main()
{
    while(scanf("%d",&n) && n !=-1)
    {
        if(n%2) {printf("0\n"); continue;}
        memset(f,0,sizeof(f));
        f[0][(1<<3)-1]=1;
        for(row=1;row<=n;++row)
        {
            dfs(0,0,0);
        }
        cout<<f[n][(1<<3)-1]<<endl;
    }

    return 0;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值