POJ No.2411

题意:用1*2的大小砖块铺设n*m的地面,求铺设方案总数。

类型:铺砖问题&状态DP

分析:关于该铺砖问题,小艾在这里提供两种DP方法。

第一种:用1*2的砖块进行铺设时,砖块可以选择横着放和竖着放两种。对于当前的位置(i, j),若横着放,则使(i, j)和(i, j+1)都置为1;若竖着放,则使(i, j)为0,(i, j+1)为1。那么对于第n行而言则必有(n, j)为1且 1<=j <= m。根据上述分析,我们可以定义一个二维的dp数组,d[i][j] := 第i行状态为j时的铺设方案总数,关于该dp数组的初始化只需枚举第一行的状态即dp[1][j],若通过检查状态j可以存在则使dp[1][j] = 1,关于状态转移方程的思考则是检查第i行的状态j能否向第i+1行的状态k转移,也就是所谓的兼容性,若二者兼容则有dp[i+1][k] += dp[i][j]。此方法的时间复杂度为O(nm2^(2m))。

第二种:我们按照选择从上到下,从左到右的顺序检查并进行砖块的铺设。对于已经铺设到的位置记为true,没有铺设到的位置记为false。那么对于当前检查到的位置(i, j),必有(i, j)之前的位置皆为true。而由于砖块的铺设可以选择横放和竖放两种,只有(i, j)到(i, m),(i+1, 0)到(i+1, j - 1)的状态是不确定的,即(i+1, j)和其后的位置一定不会有砖块放置,故皆为false。综上,我们只需枚举每一列里还没查询到的最上面的位置的状态即可,总共有m个。同样地,定义一个三维dp数组,dp[i][j][k] := 铺设位置(i, j)时状态为k时的铺设方案总数。下面我们来推导该dp的状态转移方程。对于当前的位置(i, j),设枚举的状态为used,检查位置(i, j)是否为需要放置砖块只需看used >> j & 1是否为1,若为1则不必再放置且必有(i+1, j)为0,dp[i][j][used] = dp[i][j+1][used & ~(1 << j)],否则可以在位置(i, j)上选择横放和竖放。可以横放的条件为used>>(j+1)&1 == 0并且j <= m-1,此时的铺设方案数dp[i][j+1][used | (1 << (j+1))];竖放的条件是i+1 <= n,此时的铺设方案数为dp[i][j+1][used | (1 << j)]。如果横放和竖放都可以,则有dp[i][j][used]=  dp[i][j+1][used | (1 << (j+1))] + dp[i][j+1][used | (1 << j)]。那么关于该dp数组的初始化又该如何呢?dp[n][m][0] = 1:在铺设到最后一个位置时,便于理解我们不妨在增加一列但该列的状态必须皆为false,所以第n+1列的前m-1个位置加上第n列的最后一个位置的状态只能是used=0且只能选择横放和竖放中的一种。最后只需答案输出dp[1][1][0]:在铺设第一个位置(1, 1)时其余的位置皆为false,used只能为0。此方法的时间复杂度O(nm2^m)。

总结:上述两种方法都可以通过对两个一维数组的滚动循环利用进行空间复杂度的优化,而就时间复杂度上讲第二种方法效率高于第一种。下面的参考代码是关于第二种方法的,第一种方法大家可以尝试写一下。

//实现代码

#include <iostream>
#include <algorithm>
using namespace std;

long long dp[2][1 << (15)];//1 <= n,m <= 15

void solve(int n, int m)
{
   int *cur = dp[0], *nxt = dp[1];//滚动数组的利用,dp[s]表状态为s时的方案数
   int i, j, k, s = 1 << m;
   cur[0] = 1;
   for(i = n - 1; i >= 0; --i)
   {
     for(j = m - 1; j >= 0; --j)
     {
         for(k = 0; k < s; ++k)
         {
            if(k >> j & 1)
               nxt[k] = cur[k & ~(1 << j)];
            else
            {
               int ans = 0;
               if(j + 1 < m && !(k >> (j + 1) & 1))
               ans += cur[k | (1 << (j + 1))];
               if(i + 1 < n)
                  ans += cur[k | (1 << j)];
               nxt[k] = ans;
            }
         }
         swap(cur, nxt);
      }
   }
    cout << cur[0] << endl;
}

int main()
{
     int n, m;
    cin >> n >> m;
    solve(n, m);
   return 0;
}

时间: 2024-09-30 18:50:30

POJ No.2411的相关文章

POJ 题目2411 Mondriaan&#39;s Dream(状压DP)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 13519   Accepted: 7876 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

poj 2411 Mondriaan&#39;s Dream(状压DP)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12232   Accepted: 7142 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

POJ 2411 Mondriaan&#39;s Dream

题目链接:http://poj.org/problem?id=2411 状态压缩Dynamic Programming. For each row, at ith position, 1 means that there is a block placed at this row and next row (vertically). otherwise, its 0. For the example in question, the state of For the example in que

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均为奇数的话,矩形面积就是奇数,可知是不可能完全覆盖的.接着我们来看

POJ 2411 Mondriaan&#39;s Dream(状压DP)

http://poj.org/problem?id=2411 求一个n*m矩阵用1*2方块去填满的情况有几种 思路:状压dp,先预处理那些状态之间能互相到达,情况就几种,上一个两个1,下一个状态也两个1,上一个为0,下一个必须为1,还有一种是上一个为1,下一个为0的情况 然后就一层层往后递推即可 代码: #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; int

Mondriaan&#39;s Dream - POJ 2411(状态压缩)

题目大意:有一些1*2的矩形,现在用这些小矩形覆盖M*N的大矩形,不能重复覆盖,并且要覆盖完全,求有多少种覆盖方式. 分析:可以使用1和0两种状态来表示这个位置有没有放置,1表示放置,0表示没有放置,可以有三种放置方式. 一,竖着放. 二,不放.三,横着放.直接DFS这些情况就行了................还是递归容易理解. 代码如下: =============================================================================

POJ 2411 &amp;&amp; HDU 1400 Mondriaan&#39;s Dream (状压dp 经典题)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12341   Accepted: 7204 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

POJ 2411 Mondriaan&#39;s Dream ——状压DP 插头DP

[题目分析] 用1*2的牌铺满n*m的格子. 刚开始用到动规想写一个n*m*2^m,写了半天才知道会有重复的情况. So Sad. 然后想到数据范围这么小,爆搜好了.于是把每一种状态对应的转移都搜了出来. 加了点优(gou)化(pi),然后poj上1244ms垫底. 大概的方法就是考虑每一层横着放的情况,剩下的必须竖起来的情况到下一层取反即可. 然后看了 <插头DP-从入门到跳楼> 这篇博客,怒抄插头DP 然后16ms了,自己慢慢YY了一下,写出了风(gou)流(pi)倜(bu)傥(tong)

POJ 2411 Mondriaan&#39;s Dream( 轮廓线dp )

最普通的轮廓线dp... 复杂度O(nm2min(n, m)) -------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; #define b(x) (1 << (x)) const in