例题6.1 铺放骨牌 UVa11270

1.题目描述:点击打开链接

2.解题思路:本题是最基础的轮廓线动态规划问题。这类问题的特点是无法用传统的整行整列作为状态进行dp,只能把参差不齐的轮廓线当做状态的一部分来进行转移。下面以这道题为例来谈一谈这种做法。

首先,我们需要回顾一下多阶段决策的dp问题,这类问题的解法通常是:把边界情况的dp值都设置为1,然后从小到大枚举每个阶段,在上一个阶段中的每个点j,和j的每个后继结点k,有d[cur][k]+=d[1-cur][j];其中d[cur]就是当前正在计算的阶段。本题实质上也是多阶段的决策问题,只不过需要用轮廓线来描述每个阶段。

为了降低时间复杂度,我们总是让m作为n,m中的较小者。接下来,我们按照从上到下,从左到右的顺序把棋盘划分成若干个阶段,每个阶段都有2^m个结点,其中的每个结点都用一个m位的二进制数来表示,每一位中,1表示覆盖,0表示未覆盖。阶段的决策是“以当前格子为右下角,要不要放置骨牌以及放置哪种骨牌”,这样,就有3种情况:

(1)不放,此时当前格子的上方格子必须为1,否则该决策不可行;

(2)竖放,此时当前格子不能为第一行,且当前格子的上方格子必须为0;

(3)横放,此时当前格子不能为第一列,且当前格子的上方为1,左侧为0;

这三种情况都可以用位运算加以实现,这样,本题即可按照多阶段决策的方式进行dp了,总时间复杂度为O(nm2^m)。

需要说明的一点是:本题可以写成记忆化搜索的形式,只需要在第一次清空一下memo数组,以后都不用清空了。

3.代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;

#define rep(i,n) for(int i=0;i<(n);i++)
#define me(s) memset(s,0,sizeof(s))
#define pb push_back
#define lid (id<<1)
#define rid (id<<1|1)
typedef long long ll;
typedef unsigned long long ull;
typedef pair P;


int n,m,cur;

const int maxn=15;

ll d[2][1<>m&1)d[cur][b^(1<



你可能感兴趣的:(动态规划——复杂类型,算法竞赛入门经典(训练指南))