DAG就是有向无环图。
DAG上的最长或者最短路是很重要的一类问题。很多问题都可以转化为DAG上的最长或者最短路径的问题。DAG求最短和最长路的方法一样。
本算法主要解决2个问题
1)求整个DAG中的最长路径(即不固定起点,也不固定终点)
2)固定终点,求DAG的最长路径。
1)求整个DAG中的最长路径(即不固定起点,也不固定终点)
dp【i】表示从 i 号顶点出发能获得的最长路径的长度
int DP(int i){
if(dp[i] > 0){
return dp[i];
}
for(int j = 0; j < n; j++){
if(G[i][j] != INF){
dp[i] = max(dp[i], DP(j) + G[i][j]); //状态方程
}
}
return dp[i];
}
入度为0的顶点出发最长路径长度就0,这就递归边界。
但是我们实际写的时候不妨对整个dp数组进行初始化为0,这样当dp函数访问的顶点i的出度为0的时候就会返回值0.(以此作为递归边界)。而出度不为0的就会自动递归求解。递归过程中,遇到已经计算过的顶点则直接返回对应的dp值,于是从程序逻辑上按照逆拓扑排序进行。
求路径
int DP(int i){
if(dp[i] > 0) return dp[i];
for(int j = 0; j < n; j++){
if(G[i][j] != INF){
int temp = DP(j) + G[i][j]; //单独计算以防调用Dp函数2次。
if(temp > dp[i]){
dp[i] = temp;
choice[i] = j; //j是i的后继
}
}
return dp[i];
}
}
void printPath(int i){
printf("%d", i);
while(choice[i] != -1){
i = choice[i];
printf("->%d", i);
}
}
如果有多条路径开个vector数组即可,并且 这个是自动按字典序最小输出的。因为你循环的时候是从小到大循环找路径的。
至此,零dp【i】 表示从i 顶点出发的最长路径已经求出来了。那么以i顶点结尾的最长路径长度怎么求呢?
2)固定终点,求DAG的最长路径。
可以想象只要把求解公式变一下就可以了
dp[i] = max(dp[j] + G[j][i]);
求解顺序变为拓扑排序
但是却不能直接得到字典序求出。
上面讨论的基础上,我们来讨论第二个问题,固定终点,求DAG的最长路径长度。假设终点为T,令dp【i】表示从i好顶点出发到达终点T能获得的最长路径长度。
那这与第一个有什么不同呢?
没错就是边界,第一求得边界是出度为0的点,dp值也为0,这个是求T点的DP值。
那么dp【T】 = 0
那么我们能不能还把其他的dp位置还设置为0呢?显然不行,因为由于冲没写顶点出发可能无法到达终点T,如果dp还是初始化为0那么就会得到错误的结果,我们应该初始化为-INF,如果是 +INF 会带来麻烦的(因为我们求的最长路)求最短路反之。
然后我们需要设置一个vis数组,表示这个顶点是否访问过
int DP(int i){
if(vis[i]) return dp[i];
vis[i] = true;
for(int j = 0; j < n; j++){
if( G[i][j] != INF){
int temp = max (dp[i], DP(j) + G[i][j]);
dp[i] = temp;
}
}
return dp[i];
}
矩形嵌套问题: