最短路算法合集
noip快要考了发现spfa不会打的我决定来总结一下最短路算法。
dijkstra
dijkstra基于最短路的最优子结构性质。设\(s(u, v)\)表示u到v的最短路,若k是它们最短路间的点,那么\(s(u, k)+s(k, v)=s(u, v)\)。这个用反证法就可以证。所以如果求出了当前部分点的最短路,松弛长度最小的点S的最短路一定是通过当前点的最短路转移而来的(前提是没有负权边,不然目前松弛后长度较大的点也可能更新长度较小的点)。所以算法就成立了。注意dijkstra中,由于最多松驰m次,因此最多将堆中点的权值减去m次,用普通堆的复杂度是\(O(mlogm)=O(mlogn)\),而用斐波那契堆,可以搁置某些减权操作,使得松驰的总时间复杂度变成\(O(nlogn)\),不过我并不会0.0。
(貌似正确的)程序:
int dis[maxn*2], vis[maxn];
struct cmp{
bool operator()(int x, int y){
return dis[x]>dis[y]; }
};
priority_queue, cmp> q;
void dijkstra(int src){
memset(dis, 0x3f, sizeof(dis)); dis[src]=0;
int u, v; q.push(src);
for (int i=1; i<=n; ++i){ //出n个点
while (vis[u=q.top()]) q.pop(); vis[u]=1;
for (int j=fir[u]; j; j=e[j].nxt){
v=e[j].to;
if (dis[u]+e[j].v
这样写是错的!这种情况下stl无法正确维护优先队列。所以必须要用\(greater
不过为了更简洁,就这样吧……
spfa
首先要说明bellman-ford算法的正确性。从s开始到i的最短路,由于不存在回路,不包括s经过的节点数一定少于n。由于最短路满足最优子结构性质,所以一个点的最短路一定会在n次松弛内被求出来。负环除外。spfa就是只考虑松弛后的结点的一个优化而已。
floyd
floyd的本质是动态规划,至于为什么把k放在外层,是因为k是动态规划状压的状态。若用\(f[k][i][j]\)表示可以通过1到k中结点的ij最短路路径,\(f[k][i][j]=min(f[k-1][i][j], f[k-1][i][k]+f[k-1][k][j])\),而最外面一层空间被省略了,因为状态里面有k的通通不会更新。所以能保证正确性。
也可以从矩阵的角度来理解。考虑求点对之间走x步的方案数,其实相当于x个邻接矩阵相乘。
如何证明呢?设现在的情况是走x步的矩阵X和邻接矩阵Y相乘。那么X的第i行第j列,就表示从i到j的方案数。Y的第i行第j列表示从i到j的连通性。那么,新的从i到j的方案数,事实上就是选一个点k多走一步的方案数。因此,X的第i行乘上Y的第j列,表示的就是从i到k再到j的方案数。时间复杂度\(O(n^4)\)
由于floyd要求的是最短路,我们发现可以用矩阵快速幂优化到\(O(n^3\log n)\)!!!欸欸欸,floyd不是\(n^3\)的嘛……
这是因为Floyd只乘了矩阵的第k行,乘了n次。