DAG 上的动态规划(一)

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值