POJ 476 学妹去搬砖

题意:给出一个M * N的矩阵,用1*2的方块去覆盖。但是有一些位置有花花草草,是没法放方块的。求不覆盖花花草草且把矩阵全用方块覆盖的方案数。

思路:图论上的完美覆盖问题。求出完美覆盖的个数。可以利用状态压缩DP来求解。

          我们从最左上方的格子开始覆盖,这样我们放方块的方法就是两种:覆盖当前格子右边的横着放,覆盖当前行的下一行的竖着放(有这个顺序非常重要,这样就可以DP按照顺序去求解)。然后就枚举所有的情况,判断是否是合法的覆盖。但是这样朴素的做法一定会超时,我们要去优化。

         设要在(i,j)格子上放置方块,由于总是从最左上方开始放置的,则对于(i' ,j') < (i,j)总可以保证(i',j')已经放完。同时,由于方块的大小为1*2,那么,对于满足

(i',j') >=(i,j)且除了最小的i'之外 的格子,一定是没有被覆盖的。所以,其实真正的不确定的只有每列还没有查询的格子最上面的一个,一共m个。则这m个可以通过状态压缩来编码进行记忆化搜索。

另一个思路:前面已经说到这是图论中的完美匹配,所以我们可以利用图论完美匹配的解法去解这道题。因为上一个思路是指数级的复杂度,所以在很大的图中是不适用的,那这个思路就提供了很好的方法。

代码如下:

#include <bits/stdc++.h>
 
using namespace std;
const int MOD = 1000000007;
const int MAX = 15;
int M,N,K;
int dp[1<<MAX];
int res[1<<MAX];
bool f[MAX][MAX];
 
int main(void)
{
    //freopen("input.txt","r",stdin);
    while(scanf("%d %d %d", &N,&M,&K) != EOF){
        memset(f,false,sizeof(f));
        memset(res,0,sizeof(res));
        memset(dp,0,sizeof(dp));
 
        for(int i = 0 ; i < K; ++i){
            int x,y;
            scanf("%d %d", &x, &y);
            f[x][y] = true;
        }
         
        res[0] = 1;
        for(int i = N-1 ; i >=0; --i){
           for(int j = M-1 ; j >=0 ;--j){
               for(int s = 0; s < (1<<M); ++s){
                    if((s & (1 << j)) || f[i][j])
                        dp[s] = res[s &(~(1 << j))];
                    else{
                        int ans = 0;
                        if(j+1 < M && !f[i][j+1] && !(s & (1<<(j+1))))
                            ans = (ans + res[s | (1 << (j+1))]) % MOD;
                        if(i+1 < N && !f[i+1][j])
                            ans = (ans + res[s | (1 << j)]) % MOD;
                        dp[s] = ans;
                    }
                }
                swap(dp,res);
            }
        }
        printf("%d\n",res[0]);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值