有这么几种最短路算法:floyd、dijkstra、bellman-ford、spfa
一、floyd:
基本过程:枚举中间点,然后枚举起点和终点,用中间点去更新起点到终点的距离。
证明:用每个点去更新所有它能更新的点,是不是就是最短路了?(然而并不会证明)。
多源最短路、求最小环、判断负环、适合稠密图
求最小环:在求最大标号为n的最小环时,预习求出1-n-1的最短路,然后找出两个点i,j,使得i到j的距离(不会经过n,因为只求了1-n-1的最短路)加上i和j到n的距离的和最小,这就是要求的最小环。
判断负环:就是边求边判断,如果存在点到自己的距离小于0,那么就出现了负环。
模板题codevs 1557 热浪
//我的floyd只有70分,不知道floyd能不能ac这个题
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
if (i!=k)
for (int j=1;j<=n;j++)
if (j!=k&&j!=i)
g[i][j]=min(g[i][j],g[i][k]+g[k]
二、dijkstra:
基本过程:和上一篇的prim差不多,改变就是不再是到最小生成树的距离,而是到起点的距离,即每次找离起点最近的点放入最短路。
证明:对于新加进来的肯定不会有更短的路了,虽然对这个点只有在它之前求出最短路的来更新,但是在这个点之后进来的点一定不能更新这个点到起点的距离。
单源最短路、求最小环、判断负环、适合稠密图、不能处理带有负边权的图
求最小环:不断地删去某条边,求出删去之后这条边起点到终点的最短距离,再加上这条边的长度就是目前求的环的长度,求出最小的就行了。
判断负环:临时想出来的。。如果新进来的可以更新已经求出最短路的点的距离那么就存在负环。
for (int i=1;i<=n;i++)
{
int k=0;
for (int j=1;j<=n;j++)
if (!f[j]&&dis[j]<dis[k])
k=j;
f[k]=1;
for (int j=1;j<=n;j++)
if (!f[j]&&dis[j]>dis[k]+g[k][j])
dis[j]=dis[k]+g[k][j];
}
三、bellman-ford:
基本过程:进行n-1遍对所有边的松弛操作。
证明:每次至少将一条边变成最终松弛状态(就是不会再被用来松弛了)吧,然后图中最长的最短路长度为n-1。
单源最短路、求最小环、判负环、适合稀疏图
求最小环:和dijkstra差不多去删边。
判断负环:在求完最短路之后如果还有边可以松弛,那么就存在负环。
for (int i=1;i<=n-1;i++)
for (int j=1;j<=2*m;j++)
{
a=bian[j].from;b=bian[j].to;
dis[b]=min(dis[b],dis[a]+bian[j].l);
}
四、spfa:
基本过程:通过队列,不断地松弛一些边,因为这些边的终点到起点的距离变小了,那么这些终点连接的其他点也可能可以更新,所以把这些终点们加入队列,只要队列里还有点就要继续进行,直到队列为空方才求出最短路。
证明:只要可能能更新其他点的都进队了,现在队列空了,下面的事情。。。
单源最短路、求最小环、判断负环、适合稀疏图
求最小环:应该还是删边吧,另外附上一句上面求最小环除了floyd我都没有试过(~(≧▽≦)/~啦啦啦)。
判断负环:关于spfa判断负环我只会一个比较水方法,就是如果一个点进队次数超过n次那么就存在负环,至于为什么,我并不知道。
int head=1,tail=1;
memset(dis,127/2,sizeof(dis));
dui[1]=s;f[s]=1;dis[s]=0;
int a,b,len;
while (head<=tail)
{
a=dui[head];
len=bian[a].size();//bian是vector版本的邻接表。
for (int i=0;i<len;i++)
{
b=bian[a][i].to;
if (dis[b]>dis[a]+bian[a][i].l)
{
dis[b]=dis[a]+bian[a][i].l;
if (!f[b])
{
tail++;
dui[tail]=b;
f[b]=1;
}
}
}
head++;
f[a]=0;
}