DAG 模型一 嵌套矩形问题
问题描述:
嵌套矩形问题。 有n个矩形,每个矩形可以用两个整数a,b描述,表示它的长和宽。矩形X(a,b)可以嵌套在矩形Y(c,d)中,当且仅当 a小于c,b小于d,或者,b小于c,a小于d。例如X(1,5)能嵌套在Y(6,2)中,但不能嵌套在(3,4)中。你的任务是选出尽量多的矩形排成一行,使得除最后一个外,每一个矩形都可以嵌套在下一个矩形内。如果有多解,矩形编号的字典序应尽量小。
–摘自刘汝佳《算法竞赛入门经典》9.2章
解答思路:
矩形的“可嵌套”关系是一个典型的二元关系,可用图来构造模型求解。显然,矩形的嵌套关系可用DAG来表示。并且,本题构造出来的DAG是无环的。而我们所求能嵌套的矩阵长度则可转化为求解DAG上的最长路径问题。
而题目没有给出最长的路径明确的起点和终点,因此,每个节点都可能是最优的起点,需要遍历各个顶点一次。
输入数据及初始化:
6 //矩形个数
3 4 //每个矩形的长和宽
2 5
1 2
5 6
4 5
4 5
用邻接矩阵来描述图。
构造完成的DAG及手动求解的正确答案,如下图所示:
状态转移方程:
设depths[i] 表示从节点出发的最长路径长度,第一步只能走到他的相邻节点,因此转移方程为:
depths[i] = max{ depths[j] + 1 | grid[i][j]=1}
初始化时,depths[] 数组应初始化为0。
由于需要求解字典序最小的路径,因此,动态规划出各个节点depth之后,按节点顺序从小到大遍历depth,记录最大depath节点的节点号,即为起点。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
using namespace std;
#define LL long long
const int maxn = 100 + 5;
int n;
int grid[maxn][maxn]; //邻接矩阵
int depths[maxn]; //各个节点的嵌套矩阵深度
//矩阵定义
struct Mretrangle {
int width;
int height;
Mretrangle() {}
Mretrangle(int x, int y) : width(x), height(y) {}
//自定义嵌套关系
bool operator<(const Mretrangle& b) const {
if (width < b.width && height < b.height) {
return true;
}
if (width < b.height && height < b.width) {
return true;
}
return false;
}
} m_retrangles[maxn];
//动态规划和深度优先搜索
int dp(int i) {
int& ans = depths[i];
if (ans > 0) {
return ans;
}
ans = 1;
for (int j = 0; j < n; j++) {
if (grid[i][j]) {
ans = max(ans, dp(j) + 1);
}
}
return ans;
}
//打印字典序最小的路径
void print_path(int i, bool flag) {
if (!flag) {
cout << "->" << i;
}
for (int j = 0; j < n; j++) {
if (grid[i][j] && depths[i] == depths[j] + 1) {
print_path(j, false);
break;
}
}
}
int main() {
freopen(
"/Users/yuxiao/XcodeProject/DynamicalProgramming/DynamicalProgramming/in",
"r", stdin);
freopen(
"/Users/yuxiao/XcodeProject/DynamicalProgramming/DynamicalProgramming/"
"out",
"w", stdout);
int T, t;
cin >> T;
t = 1;
int ans;
while (T--) {
cin >> n;
ans = 0;
int x, y;
memset(grid, 0, sizeof(grid));
for (int i = 0; i < n; i++) {
cin >> x >> y;
m_retrangles[i] = Mretrangle(x, y);
}
//构造图
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (j != i) {
if (m_retrangles[i] < m_retrangles[j]) {
grid[i][j] = 1; //构造边
}
}
}
}
// for (int i = 0; i < n; i++) {
// for (int j = 0; j < n; j++) {
// if (grid[i][j]) {
// cout << i << "->" << j << " " << grid[i][j] << endl;
// }
// }
// }
memset(depths, 0, sizeof(depths));
//记录起点
int first = -1;
for (int i = 0; i < n; i++) {
depths[i] = dp(i);
if (ans < depths[i]) {
ans = depths[i];
first = i;
}
}
cout << first;
//打印路径
print_path(first, true);
cout << endl;
// cout << endl;
printf("Case #%d: %d\n", t++, ans);
}
return 0;
}