一、题意
有n(n<=30)种立方体,每种有无穷个。现在要求你选一些立方体摞成尽量高的一个柱子,上面的立方体底面长宽必须严格小于下面的立方体。问最大高度。
二、解析
由于上面的立方体底面长宽必须严格小于下面的立方体,因此不会出现环,也就是说这是一个典型的DAG(有向无环图)问题。
有向无环图可以通过记忆化搜索(动态规划)的方式来解决,因此直接套用模板即可。
唯一要注意的是这道题里的状态需要用两个参数来表示,即i, k。i表示这是第几种立方体,k表示这个立方体哪条边作为高。因此记忆化搜索是状态的存放数据memo也应该是两维的。
三、代码
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn = 30 + 5;
int kase = 1, n, a[maxn][3], memo[maxn][3];
bool isFit(int v, int vk, int u, int uk) {
int va = a[v][(vk + 1) % 3], vb = a[v][(vk + 2) % 3];
int ua = a[u][(uk + 1) % 3], ub = a[u][(uk + 2) % 3];
return va < ua && vb < ub || va < ub && vb < ua;
}
int dp(int u, int uk) {
int & res = memo[u][uk]; // 记忆化搜索
if(res != -1) return res;
res = a[u][uk];
for(int v = 0; v < n; v ++) for(int vk = 0; vk < 3; vk ++) {
if(isFit(v, vk, u, uk)) res = max(res, a[u][uk] + dp(v, vk));
}
return res;
}
int main() {
while(cin >> n && n) {
for(int i = 0; i < n; i ++) cin >> a[i][0] >> a[i][1] >> a[i][2];
memset(memo, -1, sizeof(memo));
int ans = 0;
for(int i = 0; i < n; i ++) for(int k = 0; k < 3; k ++) ans = max(ans, dp(i, k));
cout << "Case " << kase ++ << ": maximum height = " << ans << endl;
}
}四、归纳
- 当问题中出现单向依赖关系时,可以考虑转化为DAG问题进行处理
- DAG问题的最长路问题可以通过记忆化搜索来解决,在DAG最长路问题中,dp(i)表示以i为起点的最长路。
模板大致为:
int dp(int u) {
int & res = memo[u]; // 记忆化搜索
if(res != -1) return res;
res = ...;
for(int v = 0; v < n; v ++) if([依赖条件]) {
res = max(res, dp(v, vk) + ....);
}
return res;
}- 通过引用的方式引用memo,就不容易忘记记录结果到memo中
- 记忆化搜索的时间复杂度 = 状态数 * 每个状态的决策数量。在本题中就是 3n * 3n = O(n^2)

京公网安备 11010502036488号