铺砖问题 (状态压缩DP)

题意:

给定n*m的格子,每个格子被染成了黑色或者白色。现在要用1 * 2 的砖块覆盖这些格子,要求块与块之间互相不重叠,且覆盖了所有白色的格子,但不覆盖任意一个黑色格子。求一个有多少种覆盖方法,输出方案数对M取余后的结果。

输入:

n= 3

m= 4

每个格子的颜色如下所示(.表示白色,x表示黑色)

.x.

输出:

2

分析:

由于黑色的格子不能被覆盖,因此used里对应的位置总是false。对于白色的格子,如果现在要在(i,j)位置上放置砖块,那么由于总是从最上方的可放的格子开始放置,因此对应(i’,j’)<(i.j)(按字典序比较)的(i’,j’)总有 used[i’][j’]=true成立。

此外,由于砖块的大小为1*2,因此对于每一列j’在满足(i’,j’)>=(i.j)的所有i’中,除了最小的i’之外都满足used[i’][j’]=false。因此,不确定的只有每一列里还没有查询的格子中最上面的一个,共m个。从而可以把这m个格子通过状态压缩编码进行记忆化搜索。按照之前的状态压缩DP的写法就得到了下面的程序。

int dp[1 << maxn];      //DP数组(滚动数组循环利用)

void solve()
{
    int *crt = dp[0], *next = dp[1];
    crt[0] = 1;
    for (int i = n - 1; i >= 0; i--){
        for (int j = m - 1; j >= 0; j--){
            for (int used = 0; used < 1 << m; used++){
                if ((used >> j & 1) || color[i][j]){
                    //不需要在(i, j)放置砖块
                    next[used] = crt[used & ~(1 << j)];
                }
                else{
                    //尝试2种放法
                    int res = 0;
                    //横着放
                    if (j + 1 < m && !(used >> (j + 1) & 1) && !color[i][j + 1]){
                        res += crt[used | 1 <<< (j + 1)];
                    }
                    //竖着放
                    if (i + 1 < n && !color[i + 1][j]){
                        res += crt[used | 1 << j];
                    }
                    next[used] = res % M;
                }
            }
            swap(crt, next);
        }
    }
    printf("%d\n", crt[0]);
}

时间: 2024-11-10 18:04:21

铺砖问题 (状态压缩DP)的相关文章

poj 2411 Mondriaan&#39;s Dream(状态压缩+dp)

 题意:用1*2砖块铺满n*m的房间. 思路转自:http://www.cnblogs.com/scau20110726/archive/2013/03/14/2960448.html 因为这道题输入范围在11*11之间,所以可以先打表直接输出.......... 状态压缩DP 经典覆盖问题,输入n和m表示一个n*m的矩形,用1*2的方块进行覆盖,不能重叠,不能越出矩形边界,问完全覆盖完整个矩形有多少种不同的方案 其中n和m均为奇数的话,矩形面积就是奇数,可知是不可能完全覆盖的.接着我们来看

Mondriaan的梦(状态压缩dp)

题目原题可以看POJ2411,大意是给出m*n的矩形,要用2*1的矩形将它铺满(不能讲一个矩形铺在另外一个上面),求方案数,并且只要不是完全相同的就算不同的方案,也就是对称算不同的方案. F[i][s]表示前i-1行已经填满,并且第i行的状态是s的方案数. F[i][s]=sum(F[i-1][s’]): s‘能转移到s.如何根据s来确定s'呢.这里用一个dfs实现,一位一位去填充s'.扫描s,如果该位是1,那么s’对应的这位必定是0,如果该位是0,那么要分情况讨论: 如果该位的后面一位也是0,

pojPOJ 2411--Mondriaan&#39;s Dream+状态压缩dp

又是一道经典的状态压缩dp 开始自己想了一下,总是觉得因为这个小矩形可以竖着放导致没法确定状态如何转移(第i行的小矩形如果竖着放,及可能影响i-1行,也有可能影响i+1行);后面看了别人的题解后,才知道原来我们可以固定小矩形竖着放的时候只能向前放,这样第i行的状态就只能影响i-1行了,也就能顺利的写出状态转移方程啦. 设dp[i][j]表示第i行处于状态j的时候,共有多少种放置方法. dp[i][j]=sum(dp[i-1][k]),其中状态j和k要能共存,并且j和k要使得第i-1行刚好铺满.

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反

HDU1565(状态压缩dp)

方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8170    Accepted Submission(s): 3095 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数

HDU 3001【状态压缩DP】

题意: 给n个点m条无向边. 要求每个点最多走两次,要访问所有的点给出要求路线中边的权值总和最小. 思路: 三进制状态压缩DP,0代表走了0次,1,2类推. 第一次弄三进制状态压缩DP,感觉重点是对数据的预处理,利用数组分解各个位数,从而达到类似二进制的目的. 然后就是状态的表示,dp[s][i]表示状态s时到达i的最优值. 状态转移也一目了然,不废话. #include<stdio.h> #include<string.h> #include<algorithm> u

Victor and World(spfa+状态压缩dp)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5418 Victor and World Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/131072 K (Java/Others)Total Submission(s): 958    Accepted Submission(s): 431 Problem Description After trying hard fo

poj 3311 Hie with the Pie(状态压缩dp)

Description The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as possible. Unfortunately, due to cutbacks, they can afford to hire only one driver to do the deliveries. He will wait for 1 or more (up to 10) orders to be

HDU--1074(状态压缩DP)

典型的状态压缩DP,给出了每件作业的截止时间和花费,求让老师扣分最少的写作业方式.把完成n种作业用状态2^n-1表示,dp[s]表示 完成状态s时,最小扣分.比如“111”,那么可以由“011”,“110”,“101”转移过来,分别表示选了0,1号作业,1,2号作业,0,2号作业. t[s]表示状态S记录的总时间.dp[s] = min{dp[j]+c[k] - d[k]},其中j = i^(1<<k),0<k<n;pre[s]表示状态s完成时,最末尾完成的作业, #include