最短路问题-基础篇

先写篇博客回复一下心情吧
图论太难了,我不配
总结一下这几天学的吧。
朴素版dijkstra算法
时间复杂是 O(n2+m), n 表示点数,m 表示边数
主要用于解决边权为正,存在重边和自环;
参考链接

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=510,INF=0x3f3f3f3f;
int n,m;//n个点,m个边;
int g[N][N];//稠密图用邻接矩阵存图;
int d[N];
bool is[N];
void dijkstra(){
    for(int i=0;i<n-1;i++){
        int t=-1;
        
        for(int j=1;j<=n;j++)
            if(!is[j]&&(t==-1||d[t]>d[j]))t=j;//找出未标记且离起点最近的点
            
            is[t]=true;//标记起点
            
        for(int j=1;j<=n;j++)//用新确定的t点,去更新其他所有点
            d[j]=min(d[j],d[t]+g[t][j]);
            
        
    }if(d[n]==INF)cout<<"-1";//d[n]==INF说明不连通
    else cout<<d[n];
}
int main(){
    memset(g,INF,sizeof g);
    memset(d,INF,sizeof d);//这三行初始化;
    d[1]=0;
    cin>>n>>m;
    int x,y,z;
    while(m--){
        cin>>x>>y>>z;
        g[x][y]=min(g[x][y],z);//解决邻接矩阵存储的有向图重边问题
    }dijkstra();
    return 0;
}

堆优化版dijkstra
时间复杂度 O(mlogn)), n 表示点数,m 表示边数
参考链接

#include<iostream>
#include<queue>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
typedef pair<int,int>PII;
priority_queue<PII,vector<PII>,greater<PII>> que;//定义一个小根堆
const int N=150010,INF=0x3f3f3f3f;
int n,m;
bool is[N];
int e[N],ne[N],w[N],h[N],idx;//邻接矩阵,用来存储稀疏图
int d[N];//某点到n点的距离
void add(int x,int  y,int z){
    e[idx]=y,w[idx]=z,ne[idx]=h[x],h[x]=idx++;
}
void dijkstra(){
    que.push({0,1});//第一个值代表距离,第二个点代表某点
    while(que.size()){
       PII t=que.top();//取出离源点最近且未标记的点
       que.pop();
       int dic=t.first,ver=t.second;
       if(is[ver])continue;
       is[ver]=true;
       for(int i=h[ver];i!=-1;i=ne[i]){
           int j=e[i];
           if(d[j]>dic+w[i]){//遍历已确定点的每个相邻的点
               d[j]=dic+w[i];
               que.push({d[j],j});//如果进行了一次更新进放入队列中
           }
       }
    }if(d[n]==INF) cout<<-1;
    else cout<<d[n];
}
int main(){
    memset(h,-1,sizeof h);
    memset(d,INF,sizeof d);
    d[1]=0;
    cin>>n>>m;
    int x,y,z;
    while(m--){
        cin>>x>>y>>z;
        add(x,y,z);
    }dijkstra();
    return 0;
}//优先队列本身保证了去最近的点,取出该点后,看该点是否被标记过,
//标记过则contonue,未标记则标记,然后遍历所取点的每一个点,更新所有相邻点
//将更新后的点放入队列中;

bellman-ford算法
时间复杂度 O(nm), n 表示点数,m 表示边数
参考链接
一般用于解决有边数限制的最短路问题,边权可以为负

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=510,M=10010,INF=0x3f3f3f3f;
int d[N],back[N];
int n,m,k;
struct edges{
    int a,b,w;
}e[M];
void bellman_ford(){
    for(int i=0;i<k;i++){//最多通过的点数
        memcpy(back,d,sizeof d);//复制上一次的状态
        for(int j=0;j<m;j++){//遍历每一条边
            int a=e[j].a,b=e[j].b,w=e[j].w;
            d[b]=min(d[b],back[a]+w);//对每一条边进行松弛操作
            }
        }
    if(d[n]>INF/2)cout<<"impossible";//因为存在负权边,所以只需大于INF/2,即可判定不连通;
    else cout<<d[n];
}
int main(){
    memset(d,INF,sizeof d);
    d[1]=0;
    cin>>n>>m>>k;
    int a,b,w;
    for(int i=0;i<m;i++){
        cin>>a>>b>>w;
        e[i]={a,b,w};
    }bellman_ford();
    return 0;
}

spfa算法(队列优化的Bellman-Ford算法)
时间复杂度 平均情况下 O(m),最坏情况下 O(nm), n表示点数,m 表示边数
参考链接1
参考链接2

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e5+10;
int e[N],ne[N],w[N],h[N],idx;
int d[N],n,m;
bool is[N];
void add(int a,int b,int c){//建立链表;
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa(){
    d[1]=0;
    queue<int> q;
    q.push(1);
    while(q.size()){
        auto t=q.front();
        is[t]=false;
        q.pop();
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(d[j]>d[t]+w[i]){
                d[j]=d[t]+w[i];
                if(!is[j]){
                    q.push(j);
                    is[j]=true;
                }
            }
        }
    }if(d[n]==0x3f3f3f3f)cout<<"impossible";
    else cout<<d[n];
}
int main(){
    memset(h,-1,sizeof h);
    memset(d,0x3f,sizeof d);
    cin>>n>>m;
    while(m--){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }spfa();
    return 0;
}

多元路问题的floyd算法
时间复杂度是 O(n3), n表示点数
参考链接

#include<bits/stdc++.h>//多源路问题,用一个邻接矩阵,基于动态规划;
using namespace std;
const int N=510,INF=0x3f3f3f3f;
int g[N][N];
int n,m,w;
void fol(){
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
            }
        }
    }
}
int main(){
    cin>>n>>m>>w;
    memset(g,INF,sizeof g);
    for(int i=1;i<=n;i++)g[i][i]=0;
    int x,y,z;
    while(m--){
        cin>>x>>y>>z;
        g[x][y]=min(g[x][y],z);//解决重边问题,
    }fol();
    while(w--){
        cin>>x>>y;
        if(g[x][y]>INF/2)puts("impossible");
        else cout<<g[x][y]<<"\n";
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值