POJ1797 Heavy Transportation

变种Dijkstra/部分最大生成树

Memory: 1860K Time: 344MS
https://code.csdn.net/snippets/1632598

题意:找到一条从1到n的路径,使得路上的最小边权最大

这道题乍看之下,没有现成的算法能解决(其实也确实没有),如果想到最大生成树,见#出现的错误一栏
更正一下,最大生成树能做。但是需要注意一些问题。见#出现的错误

回顾一下我们求单纯最短路径时利用的性质

最短路径的最优子结构性质

该性质描述为:如果P(i,j)={Vi….Vk..Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。这是显而易见的,否则就明显存在一条更短的路径
由上述性质可知,如果存在一条从i到j的最短路径(Vi…..Vk,Vj),Vk是Vj前面的一顶点。那么(Vi…Vk)也必定是从i到k的最短路径。

通过这个十分有用的性质,伟大的Dijkstra 发明了一个高效的算法

为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法。譬如对于源顶点V0,首先选择其直接相邻的顶点中长度最短的顶点Vi,那么当前已知可得从V0到达Vj顶点的最短距离dist[j]=min{dist[j],dist[i]+matrix[i][j]}。根据这种思路,
假设存在G=,源顶点为V0,U={V0},dist[i]记录V0到i的最短距离,path[i]记录从V0到i路径上的i前面的一个顶点。
1. 从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;
2. 更新与i直接相邻顶点的dist值。(dist[j]=min{dist[j],dist[i]+matrix[i][j]})
3. 知道U=V,停止。

所以,满足最优子结构性质的任何类似最短路的要求都可以化身成同样的思路解决。
那么让最小边权最大的路径是否满足这个性质呢?
设路径经过i(start), j, k(end),则路径长度dis(i, k)=min(dis(i, j), dis(j, k))
不难想到,为了让dis(i, k)最大,必须让dis(i, j)dis(j, k)都最优,反之亦然。
既然满足这个性质,那么可以直接套修改版的Dijkstra了:(LaTeX大法好)

假设存在 G=<V,E> ,源顶点为 V0 U={V0} , dist[i] 记录 V0 i 的最短距离, path[i] 记录从 V0 i 路径上的i前面的一个顶点。
1. 从 VU 中选择使 dist[i] 值最大的顶点i,将i加入到U中;
2. 更新与i直接相邻顶点的dist值。dist[j]=max{dist[j], min(dist[i], matrix[i][j])}
3. 知道 U=V ,停止。

出现的错误

关于最大生成树

自己一开始用最大生成树写了一个,然后WA。
题解里有讨论最大生成树合理性问题的,没看清这位网友的说法,以为不能做。其实是可以的。(为了验证能不能做,找到了网上三个博客的最大生成树代码,自己交了下试试,发现可以A的)
而且最大生成树更好理解些。

先求出这张图的最大生成树再说。
下一步很重要。答案等于什么?是求出的最大生成树中边的最小值吗?那你就华丽丽地WA了。题目只要求从1到n,所以只需求从1到n的树上路径上的边权最小值即可。(就像刚才那位网友说的一样)

不断地将最大权值的边加入,遇到终点N就结束,这样就不会再找到一条可以更换当前路径的更好的路径了。

边求MST边求树上单源路径这个需求就显出了Prim的优势了。从1’点(起始点)开始直接跑,记录每个vis的点到1的路径上边权最小值,直到vis到了n点(结束点),答案就是存在它上面的边权最小值,无需再枚举。
如果用Kruskal,我暂时也只能想到记录所有MST边单独建一棵树,DFS求树上路径了。太不优雅。。

话说。。Prim不是从Dijkstra改造而来的吗。。
这两个算法不会就是一个吧(⊙o⊙)!
毕竟Prim找当前待选边中最长的边的过程,和Dijkstra找当前待选点中单源路径上最小边权最大的点的过程,在这道题里很相像,结果也应该是等价的。
这样一想,可能Dijkstra更靠谱。。
不行,不能再想了,已经够多了。。。。

你可能感兴趣的:(OI)