1. Dijkstra (朴素)
是用来求单源最短路的:求一个点到其他点的最短路
ps:(什么堆优化我不会,也懒得学了)
适合所有边权都是“正数”的情况,时间复杂度为O(n^2)
首先初始化:dist[i] = +∞, dist[st]=0 (st为起始点)
然后for循环n次:① 找出目前没有选中的点中距离最短的点,选其为当前点now
② 再用now来更新其他点的最短路距离(dist[i])
路径更新:
用二维vector比较方便,vector<int> path[MAXN]; 用二维数组我反而不会写
初始化:path[st].push_back(st); //只初始化起点就ok
用now来更新其余点时,可以理解为该点是从now点过来的,所以:
path[j]=path[now]; // 二维数组能这样直接赋值吗?!不能,所以vector yyds啊!
path[j].push_back(j); // 再把自己放进去
代码如下,其中arr为邻接矩阵,可以看出时间复杂度为O(n^2)
void Dij(int st) {
memset(dis, 0x3f3f3f3f, sizeof(dis));
memset(walked, 0, sizeof(walked));
dis[st] = 0;
path[st].push_back(st);
for (int i = 0; i < n; i++) {
int now = n; //因为dis[n]为无穷大,因此now会在下面的循环被更新
for (int j = 0; j < n; j++) {
if (dis[now] > dis[j] && !walked[j])
now = j;
}
// if(dis[now]==0x3f3f3f3f) return;
walked[now] = 1;
for (int j = 0; j < n; j++) {
if (!walked[j] && dis[j] > dis[now] + arr[now][j]) { //只用更新没被走过的点
dis[j] = dis[now] + arr[now][j];
path[j] = path[now];
path[j].push_back(j);
}
}
}
}
ps:当初自己学的时候快被折磨坏了,然后一遍遍的模拟代码执行的过程,终于懂了o(╥﹏╥)o,想想那段时光还真是痛苦
2. Floyd弗洛伊德
多源最短路:求任意两个点的最短路径
边权可正可负,但是不能出现负环!
首先初始化 dis[ i ][ j ] = +∞
然后基于动态规划的思想,看看从i到j能不能被 从i到k 加上 从k到j 代替 (即更短的路)
路径更新:
int path[MAXN][MAXN];
初始化 path[ i ][ j ] = j
如果能被更新,则有path[ i ][ j ] = k ,其含义是从i到j需要经过点k,此时再去找path[ k ][ j ],如果path[ k ][ j ] == j,则说明到达终点,否则一直找下去。
代码如下,时间复杂度为O(n^3)
void Floyd() {
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++) path[i][j]=j; //初始化中转站
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++) {
//if(dis[i][k]==MAX||dis[k][j]==MAX) continue;
if(dis[i][j]>dis[i][k]+dis[k][j]) {
dis[i][j]=dis[i][k]+dis[k][j];
path[i][j]=path[i][k]; //path[i][j]=k也可以
}
}
for(int i=1; i<=n; i++) { //从k到i,i为终点
int k=st; //st为起点
if(st==i) continue;
cout<<dis[st][i]<<endl; //输出从st到i的最短路的权值
while(k!=i) {
cout<<k<<"--";//输出路径
k=path[k][i];
}
cout<<i<<endl;
}
}
最后dis[i][j]就是从i到j的最短路径长度,因此用Floyd也可以判断出是否成环,只要从顶点i到顶点i(到他自己),即dis[i][i]≠一开始的+∞,那么就存在环。
ps:多么的!简单明了啊(除了path)!!就三个for,背都背过了,对初学者多么友好啊,总之我当时学的时候很喜欢,然后被一堆人说:时间复杂度太高啦,大一点的数据就跑不了了。我:学校oj能过就行,我管这些= =