DAG上的动态规划
—不固定起点的最长路径
例题:嵌套矩形问题:
问题描述:
有n个矩形,每个矩形可以用两个整数a,b来描述,a,b分别代表这个矩阵的长与宽。有两个矩阵A(a,b),B(c,d);当且仅当a<c且b<d 或者 a<且b<c时,A可以嵌套在B内。例如(3,4)可以嵌套在(4,5)内,也可以嵌套在(5,4)内。你的任务时选出尽量多的矩形排成一行,使得除了最后一个之外,每一个矩形都可以嵌套在下一个矩形内。如果有多解,则请输出矩形字典编号最小的方案。
输入例:
5 (指将要输入5行数据,每一行数据代表一个矩形)
3 4 (指序号为0的矩形(3,4))
7 8 (指序号为1的矩形(7,8))
1 2 (指序号为2的矩形(1,2))
9 10 (指序号为3的矩形(9,10))
5 6 (指序号为4的矩形(5,6))
输出:
3:(9,10)
1:(7,8)
4:(5,6)
0:(3,4)
2:(1,2)
分析:
这个问题的实质,实际上是一个DAG(有向无环图)中不固定起点寻找最长路径的问题。
将每一矩阵设为一个图节点,如果A矩形能把B矩形嵌套,则A矩形有一条路指向B(B没有路指向A),边的权值统一为1。按照此思想建立有向无环图模型,则矩形嵌套问题便转化为在图中找到一条最长的路径。
那么要找到着条最长路径,则需要用到动态规划思想。
实施:
(1st)
创建一个图节点结构用来存储这些矩形数据
(2nd)
用邻接矩阵数据结构和一个顺序表结构来表达整个DAG图
其中,
邻接矩阵数据结构用G[][]一个二维数组实现,用来存储节点序号与边信息;
顺序表结构用C++的STL模板中的vector类来实现,用来存储矩形节点数据。
这两种结构有着一一对应的关系,从而组成为一个完整的图。
(3rd)
设置一个顺序结构用来存储由每一个节点为起点的最长路径长度。
用d[]一维数组实现,用来存储由第i节点为起点的图中最长路径长度值
(4st)
由1,2,3步建立了此题完成的数据存储结构后,我们可以进一步求解了,即找到每个节点作为起点最长路径了
d(i) = max{d(j)+1}
其中:
d(i)表是第i个节点作为起点的最长路径长度
d(j)表示:所有i节点的可达的邻接节点为起点的最长路径长度
1表示:边的长度为1
所以:
实质是一个递推法,从起点开始一步一步递归递推得到最终的值
(5st)
由第4步得到了一个数组:一个存储了图中每一个节点为起点的最大路径长度的数组。那么我们只要遍历这个数组便能找到拥有最长路径的那个起点的序号,以及最长路径的长度
(6st)
由5步,我们找到了这个拥有最长路径的起点,那么我们只要由此起点开始,沿着这条最长路径顺腾摸瓜,便能找到该路径上的所有节点。
其中:
我们发现这条最长路径上节点之间的规律:
如果:
d[i] = d[j]+1
则j节点一定是i节点在这条路径上的下一个节点
实现:
/*--头文件---*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
/*--头文件---*/
using namespace std;
/*(1st)创建一个图节点结构用来存储这些矩形数据*/
typedef struct _node{
int key;
int a;
int b;
_node(){
key=-1;
a=0;
b=0;
}
_node(int x,int y,int k){
a=x;
b=y;
key = k;
}
}Node;
/*(1st)创建一个图节点结构用来存储这些矩形数据*/
/*(2nd)用邻接矩阵数据结构和一个顺序表结构来表达整个DAG图*/
int n; //设置图节点个数
vector <Node> v;//设置一个Node向量用来存放数据
int G[50][50];//设置图的节点矩阵
/*(2nd)用邻接矩阵数据结构和一个顺序表结构来表达整个DAG图*/
/*(3rd)设置一个顺序结构用来存储由每一个节点为起点的最长路径长度*/
int d[50];
/*(3rd)设置一个顺序结构用来存储由每一个节点为起点的最长路径长度*/
/*创建图*/
//将new_node作为图的第i个节点加入图
int addNodeToGraph(Node* new_node,int i);
/*创建图*/
/*(4st)由1,2,3步建立了此题完成的数据存储结构后,我们可以进一步求解了,即找到每个节点作为起点最长路径了*/
//考虑第i节点与j节点是否有通路
bool touchable(int i,int j);
//获取a,b之间的最大值
int maxd(int a,int b);
//返回每个节点作为起点最长路径长度
int f(int i);
/*(4st)由1,2,3步建立了此题完成的数据存储结构后,我们可以进一步求解了,即找到每个节点作为起点最长路径了*/
/*(5st)找到由第4步的到的数组的最大值对应的序号*/
int findmaxd(void);
/*(5st)找到由第4步的到的数组的最大值对应的序号*/
/*(6st)以序号i节点为起点按最长路径顺序打印其余节点编号及信息*/
void print_ans(int i);
/*(6st)以序号i节点为起点按最长路径顺序打印其余节点编号及信息*/
int main(int argc, char** argv) {
//对d数组初始化为0
memset(d,0,sizeof(d));
memset(G,0,sizeof(G));
//由输入得到节点个数
scanf("%d",&n);
//按照节点个数调整vector大小
v.reserve(n);
//得到矩形数据并创建DAG图
int a=0,b=0;
for(int i=0;i<n;++i){
scanf("%d%d",&a,&b);
Node* new_node = new Node(a,b,i);
addNodeToGraph(new_node,i);
delete new_node;
}
//(4st)得到数组值
for(int i=0;i<n;++i){
d[i]=f(i);
}
//(5st)
int max_i = 0;
max_i=findmaxd();
//(6st)
print_ans(max_i);
return 0;
}
int addNodeToGraph(Node* new_node,int i){
//1st:将新的图节点加入vector中
v.push_back(*new_node);
//2nd:根据节点特点更改G[][]矩阵的值
vector<Node>::iterator it;
int j;
for(it=v.begin(),j=0;it<v.end();++it,++j){
if(it->a>new_node->a&&it->b>new_node->b){
G[j][i] = 1;//j->i有路径
}
if(it->a>new_node->b&&it->b>new_node->a){
G[j][i] = 1;//j->i有路径
}
if(it->a<new_node->a&&it->b<new_node->b){
G[i][j] = 1;//i->j有路径
}
if(it->a<new_node->b&&it->b<new_node->a){
G[i][j] = 1;//i->j有路径
}
}
return 0;
}
bool touchable(int i,int j){
if(G[i][j]>0){
return true;
}else{
return false;
}
}
int maxd(int a,int b){
if(a>=b){
return a;
}else{
return b;
}
}
int f(int i){
int ans = d[i];
if(ans>0){
return ans;
}else{
for(int j=0;j<n;++j){
if(touchable(i,j)){
ans = maxd(ans,f(j)+1);
}
}
return ans;
}
}
int findmaxd(void){
int max_i = 0;
int max = 0;
for(int i=0;i<n;++i){
if(d[i]>max){
max=d[i];
max_i = i;
}
}
return max_i;
}
void print_ans(int i){
vector<Node>::iterator it;
it = v.begin()+i;
printf("(%d):",i);
printf("(%d,%d)\n",it->a,it->b);
for(int j=0;j<=n;++j){
/*如果:
i到j可达且d[i] = d[j]+1
则j节点一定是i节点在这条路径上的下一个节点*/
if(touchable(i,j)&&d[i]==d[j]+1){
print_ans(j);
break;
}
}
}