最短路

本文深入解析了五种经典的图算法,包括朴素Dijkstra算法、堆优化Dijkstra算法、Bellman-Ford算法、SPFA算法及其在判断负环的应用。通过具体实例和代码,详细阐述了各种算法的适用场景、时间复杂度及实现细节。
摘要由CSDN通过智能技术生成

注:本篇博客的思想来自于yxc大佬的视频讲解,按照大佬的思想自己敲了一下。

朴素Dijkstra算法

题目:Dijkstra求最短路 I

题意:该题题意就是,给你一个图,让你求出点1到n的最短路。
思路:由于该题所有边的权值都是正数,而且是一个稠密图,便比较多而且,点比较少,所以比较适合朴素的Dijstra算法。时间复杂度O(n^2)。别的算法也可以,该题没有卡spfa算法,就是哪一个稍微比较好的一点的问题。
下面是朴素Dijkstra算法,上面是spfa算法。
Refused
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 510;
int mp[maxn][maxn], vis[maxn], dis[maxn];
int n, m;
void Dijstra(){
    memset(dis, 0x3f, sizeof dis);
    dis[1] = 0;
    for(int i = 1; i < n; i++){
        int index = -1;
        for(int j = 1; j <= n; j++){
          if(!vis[j] && (index == -1 || dis[index] > dis[j])){
                index = j;
          }
            
        }
        vis[index] = 1;
        for(int j = 1; j <= n; j++){
            dis[j] = min(dis[j], dis[index] + mp[index][j]);
        }
    }
    
}
int main(){
    cin>>n>>m;
    memset(mp, 0x3f, sizeof mp);
    for(int i = 1; i <= m; i++){
        int a, b, c;
        cin>>a>>b>>c;
        
        mp[a][b] = min(mp[a][b], c);
    }
    
    Dijstra();
    
    if(dis[n] == 0x3f3f3f3f)cout<<-1<<endl;
    else cout<<dis[n]<<endl;
    
    return 0;
}

堆优化Dijkstra算法

题目:Dijkstra求最短路 II

思路:本题的所有边的权值全部为正值,所以还可以接着用Dijkstra算法,由于点和边的个数都是1~1e5,属于稀疏图,可以用堆优化的Dijkstra算法。时间复杂度O(m*log(n))
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+10;
int h[maxn], tot = 0, nex[maxn], to[maxn], w[maxn];
void add(int a, int b, int c){
    to[tot] = b;
    w[tot] = c;
    nex[tot] = h[a];
    h[a] = tot++;
}
int n, m, dis[maxn], vis[maxn];
typedef  pair<int, int>  PII;
int Dijkstra(){
    memset(dis, 0x3f, sizeof dis);
    priority_queue<PII>q;
    dis[1] = 0;
    q.push({0, 1});
    
    while(q.size()){
        PII tmp = q.top();
        q.pop();
        //cout<<tmp.second<<endl;
        if(vis[tmp.second])continue;
        vis[tmp.second] = 1;
        
        for(int i = h[tmp.second]; ~i; i = nex[i]){
            int j = to[i], val = w[i];
            
            if(dis[j] > dis[tmp.second] + val){
                dis[j] = dis[tmp.second] + val;
                q.push({-dis[j], j});
            }
        }
    }
    if(dis[n] == 0x3f3f3f3f)return -1;
    else return dis[n];
    
}
int main(){
    cin>>n>>m;
    int a, b, c;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= m; i++){
        cin>>a>>b>>c;
        add(a, b, c);
    }
    cout<<Dijkstra()<<endl;
    
   return 0;    
}

bellman_ford算法

题目:有边数限制的最短路

题意:给你一个图,让你最多使用k条路,1~n之间的最短路,因为存在负权值,所以需要bellman_ford算法,只能用这个算法(最短路的几个算法)。时间复杂度O(nm)。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+10;
struct Node{
    int u, v, w;
}edge[maxn];
int n, m, k, dis[maxn], vis[maxn], backup[maxn];
void bellman_ford(){
    
    memset(dis, 0x3f, sizeof dis);
    dis[1] = 0;
    for(int  i = 1; i <= k; i++){
        memcpy(backup, dis, sizeof(dis));//防止出现串联,就是刚更新完就用这个更新完的值去更新别的
        
        for(int j = 1; j <= m; j++){
            
            int val = edge[j].w;
            dis[edge[j].v] = min(dis[edge[j].v], backup[edge[j].u] + val);
        }
    }
    if(dis[n] > 0x3f3f3f3f/2)puts("impossible");//可能会出现这个路联通,但是使结果小于0x3f3f3f3f但是也是非常大的数(1, 5, inf),(5, n, -100),这么不会存在路,但是结果小于0x3f3f3f3f
    else cout<<dis[n]<<endl;
}

int main(){
    cin>>n>>m>>k;
    for(int i = 1; i <= m; i++){
        cin>>edge[i].u>>edge[i].v>>edge[i].w;
    }
    bellman_ford();
    return 0;
}

spfa算法

题目:spfa求最短路

思路:本题存在负权值,所以比较建议使用spfa算法,bellman_ford算法应该会超时的。
时间复杂度O(m)最坏O(nm)。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+10;
int h[maxn], tot = 0, nex[maxn], to[maxn], w[maxn];
void add(int a, int b, int c){
    to[tot] = b;
    w[tot] = c;
    nex[tot] = h[a];
    h[a] = tot++;
}

int n, m, dis[maxn], vis[maxn];

int spfa(){
    memset(dis, 0x3f, sizeof dis);
    queue<int>q;
    q.push(1);
    dis[1] = 0;
    vis[1] = 1;
    while(q.size()){
        int tmp = q.front();
        q.pop();
        vis[tmp] = 0;
        for(int i = h[tmp]; ~i; i = nex[i]){
            int j = to[i], val = w[i];
            if(dis[j] > dis[tmp] + val){
                dis[j] = dis[tmp] + val;
                if(!vis[j]){
                   q.push(j);
                   vis[j] = 1;
                }
            }
        }
    }
    if(dis[n] == 0x3f3f3f3f)return -1;
    else return dis[n];
    
}
int main(){
    cin>>n>>m;
    int a, b, c;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= m; i++){
        cin>>a>>b>>c;
        add(a, b, c);
    }
    int t = spfa();
    if(t == -1)puts("impossible");
    else cout<<t<<endl;
    
   return 0;    
}

spfa判断负环

题意:判断图中是否出现负环。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+10;
int h[maxn], tot = 0, nex[maxn], to[maxn], w[maxn];
void add(int a, int b, int c){
    to[tot] = b;
    w[tot] = c;
    nex[tot] = h[a];
    h[a] = tot++;
}

int n, m, dis[maxn], vis[maxn],cnt[maxn];

int spfa(){
    queue<int>q;
    for(int i = 1; i <= n; i++){//所有的点为起点
        vis[i] = 1;
        q.push(i);
    }
    while(q.size()){
        int tmp = q.front();
        q.pop();
        
        vis[tmp] = 0;
        
        for(int i = h[tmp]; ~i; i = nex[i]){
            
            int j = to[i], val = w[i];
            
            if(dis[j] > dis[tmp] + val){
                
                dis[j] = dis[tmp] + val;
                cnt[j] = cnt[tmp] + 1;
                
                if(cnt[j] >= n)return true;//存在负环
                if(!vis[j]){
                   q.push(j);
                   vis[j] = 1;
                }
                
            }
        }
    }
return false;    
}
int main(){
    cin>>n>>m;
    int a, b, c;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= m; i++){
        cin>>a>>b>>c;
        add(a, b, c);
    }
    int t = spfa();
    if(t == 0)puts("No");
    else puts("Yes");
    
   return 0;    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值