最短路方法总结(参考acwing,自用)

单源最短路(最短路的起始点都是1)

所有边权为正

1.朴素dijkstra:

时间复杂度:O(n^2)

应用场景:稠密图

特点:使用邻接矩阵存图

注意:无向图需记录两次

·设dis[1]=0,其他dis[i]=INF;

·循环n次{

        在所有未标号的节点中选出dis[x]最小的节点x

        对节点x标记

        对于x出发的所有边(x,y),更新d[y]=min( , )

·}

·已经标记过的点再也不会遍历

 代码略

2.堆优化版dijkstra

时间复杂度:O(mlogn)   m指边数,n指点数

应用场景:稀疏图

特点:使用邻接表存图

·设dis[1]=0,其他dis[i]=INF;

·创一个优先队列(务必注意点数在前or距离在前)

        priority_queue<PII, vector<PII>, greater<PII>> q;

·将更新过的点入队,已经确定的点出队

代码略

存在负权边

1.Bellman_ford算法

时间复杂度:O(mn)   m指边数,n指点数

特点:

        1.可以解决有边数限制的最短路

        2.可以判断是否存在负环,若有,则n-1次更新后还能更新

for n次
for 所有边 a,b,w (松弛操作)
dist[b] = min(dist[b],back[a] + w)

注意:back[] 数组是上一次迭代后 dist[] 数组的备份,由于是每个点同时向外出发,因此需要对 dist[] 数组进行备份,若不进行备份会因此发生串联效应,影响到下一个点。

return-1的判断条件写的是dist[n]>0x3f3f3f3f/2

代码略

2.SPFA

时间复杂度:一般O(m),最坏O(mn)   m指边数,n指点数

特点:对bellman的优化,避免遍历所有边。可以有负权边,但不可以存在负环!!

使用队列(没有优先,类似bfs),只是把发生改变的点入队罢了,且状态 st 数组可以从true变为false。

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N=1e5+10;
#define fi first
#define se second
typedef pair<int,int> PII;//到源点的距离,下标号
int h[N],e[N],w[N],ne[N],idx=0;
int dist[N];//各点到源点的距离
bool st[N];
int n,m;

void add(int a,int b,int c){
    e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}

int spfa(){
    queue<PII> q;
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    q.push({0,1});
    st[1]=true;
    while(q.size()){
        PII p=q.front();
        q.pop();
        int t=p.se;
        st[t]=false;//从队列中取出来之后该节点st被标记为false,
            //代表之后该节点如果发生更新可再次入队
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(dist[j]>dist[t]+w[i]){
                dist[j]=dist[t]+w[i];
                if(!st[j]){//当前已经加入队列的结点,无需再次加入队列,
                //即便发生了更新也只用更新数值即可,重复添加降低效率
                    st[j]=true;
                    q.push({dist[j],j});
                }
            }
        }
    }
    if(dist[n]==0x3f3f3f3f) return -1;
    else return dist[n];
}

int main(){
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    while(m--){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    int res=spfa();
    if(res==-1) puts("impossible");
    //注意可以最短路长度为-1
    else printf("%d",res);

    return 0;
}


作者:orzorz
链接:https://www.acwing.com/solution/content/9306/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 多源汇最短路(最短路起始点随机)

Floyd算法

时间复杂度:O(n^3)  

特点:对是否有负环,负权边没限制。比较简单。

  引入第k个点进行松弛

#include <iostream>
using namespace std;
const int N = 210, M = 2e+10, INF = 1e9;
int n, m, k, x, y, z;
int d[N][N];
void floyd() {
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
int main() {
    cin >> n >> m >> k;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(i == j) d[i][j] = 0;
            else d[i][j] = INF;
    while(m--) {
        cin >> x >> y >> z;
        d[x][y] = min(d[x][y], z);
        //注意保存最小的边
    }
    floyd();
    while(k--) {
        cin >> x >> y;
        if(d[x][y] > INF/2) puts("impossible");
        //由于有负权边存在所以约大过INF/2也很合理
        else cout << d[x][y] << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值