文章目录
最短路问题
最短路问题:在一个图中找到 起点到终点 距离最短的问题。
定义f(i,j)为点 i 到点 j之间的距离
一.Floyd算法–求多源最短路问题—可以处理负权边----时间复杂度O(n^3)
算法思想:f(i,j) = min( f(i,j) , f(i,K) + f(k,j) )
算法代码:
for(int i=1;i<=n;i++) //枚举中间边
for(int j=1;j<=n;j++) //枚举起点
for(int k=1;k<=n;k++) //枚举终点
if(e[j][k]>e[j][i] + e[i][k])e[j][k] = e[j][i] + e[i][k];
二.Dijkstra算法–求单源最短路问题–不能处理负权边—时间复杂度O(mlogm)
算法思想:用距离起点最近的边 去更新其他的边------------边只会被 比它短的一条边更新
算法代码:
void dijkstra(int start)
{
memset(dist,0x3f,sizeof dist);
dist[start] = 0;
for(int i=1;i<=n;i++)
{
//第一步找到距离当前点的最短的边(没有被确定为最短的)
int t = 0;
for(int i=1;i<=n;i++)
if(!st[i]&&dist[i]<dist[t])t = i;
st[t] = true;//已经被确定为最短了 不会被后面的边更新 因为后面的边都比它要大
for(int i=1;i<=n;i++)//更新其他节点
dist[i] = min(dist[i],dist[t]+e[t][i]);
}
}
寻找最短边的过程可以被优先队列优化
void dijkstra(int start)
{
memset(dis,0x3f,sizeof dis);
dis[be] = 0;
priority_queue<PII,vector<PII>,greater<PII>> que;
que.push({0,be});
while(que.size())
{
auto no = que.top();
que.pop();
int ver = no.second,distan = no.first;
if(st[ver])continue;
st[ver] = 1;
for(int i=h[ver];~i;i=ne[i])
{
int j = e[i];
if(dis[j]>distan+c[i])
{
dis[j] = distan + c[i];
que.push({dis[j],j});
}
}
}
if(dis[n]!=0x3f3f3f3f)return dis[n];
else return -1;
}
三.bellman-ford算法—可以处理负权边—时间复杂度O(M) 最坏O(NM) M:边的数量
算法原理:用每一条边对dist[]数组经行松弛-----去掉起点的flody算法
朴素版代码:
for(int k=1;k<=n;k++)
for(int i=1;i<=m;i++)
if(dist[end[i]]>dist[start[i]]+w[i])dist[end[i]] = dist[start[i]] + w[i];
//相当于
for(int i=1;i<=n;i++)
for(int k=1;k<=n;k++)
if(e[1][i]>e[1][k]+e[k][i])e[1][i] = e[1][k] + e[k][i];
队列优化—也称spfa算法:
在松弛的过程中 有一些点可能没经过n-1轮就找到了它的最短路。所以我们只用将每次被松弛的点放入队列中就好(如果它这一轮没被松弛,那么它的缩短距离的效果就已经没用了)
算法代码:
int spfa()
{
memset(dist,0x3f,sizeof dist);
dist[0] = 0;
st[0] = true;
queue<int> q;
q.push(0);
while(q.size())
{
int x = q.front();
q.pop();
st[x] = false;
for(int i=h[x];~i;i=ne[i])
{
int j = e[i],w = c[i];
if(dist[j]>dist[x]+w)
{
dist[j] = dist[x] + w;
if(!st[j])
{
st[j] = true;
q.push(j);
}
}
}
}
}