最短路问题模版总结(Dijkstra/BF/SPFA/Floyd)

在这里插入图片描述
以下的模版题都是求从1到n的最短路

AcWing 849. Dijkstra求最短路 I

朴素写法

  • 邻接矩阵表示,注意重边。
  • 重边处理:留下最小的即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=510;
const int INF=0x3F3F3F3F;

int n,m;
int g[maxn][maxn];
int d[maxn];
bool vis[maxn];

void dijkstra(int s){
    fill(d,d+maxn,INF);
    fill(vis,vis+maxn,false);
    d[s]=0;
    for(int i=0;i<n;i++){
        int u=-1,MIN=INF;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&d[j]<MIN){
                MIN=d[j];
                u=j;
            }
        }

        if(u==-1) return;
        vis[u]=true;
        for(int v=1;v<=n;v++){
            if(!vis[v]&&g[u][v]!=INF&&g[u][v]+d[u]<d[v]){
                d[v]=g[u][v]+d[u];
            }
        }

    }

}


int main(){
    cin>>n>>m;
    fill(g[0],g[0]+maxn*maxn,INF);
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        //注意有可能发生重边
        g[a][b]=min(g[a][b],c);
        //如果没有重边g[a][b]=c;
    }

    dijkstra(1);
    if(d[n]==INF) printf("-1");
    else{
        printf("%d",d[n]);
    }
}

AcWing 850. Dijkstra求最短路 II

  • 邻接表存储。 邻接表没法在读入的时候就删除重边,但过程中会取最小。
  • 优先队列优化 出队的如果已经访问过了,直接continue,忽略它即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5;
const int INF=0x3F3F3F3F;
typedef pair<int,int> PII;
struct Node{
    int v,dis;
};
int n,m;
vector<Node> g[maxn];
int d[maxn];
bool vis[maxn];

void dijkstra(int s){
    fill(vis,vis+maxn,false);
    fill(d,d+maxn,INF);
    d[s]=0;

    priority_queue<PII,vector<PII>,greater<PII>> heap;
    heap.push({0,s});
    while(!heap.empty()){
        PII top=heap.top();
        heap.pop();
        int u=top.second;
        int MIN=top.first;
        if(vis[u]) continue;
        vis[u]=true;

        for(int i=0;i<g[u].size();i++){
            int v=g[u][i].v;
            int dist=g[u][i].dis;
            if(!vis[v]&&d[u]+dist<d[v]){
                d[v]=d[u]+dist;
                heap.push({d[v],v});
            }

        }
    }



}


int main(){
    cin>>n>>m;

    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        //注意有可能发生重边 但邻接表存就不用管了
        Node node;
        node.v=b;
        node.dis=c;
        g[a].push_back(node);
    }

    dijkstra(1);
    if(d[n]==INF) printf("-1");
    else{
        printf("%d",d[n]);
    }


}

AcWing 853. 有边数限制的最短路

bellman-ford

for n次迭代(n为点数)
	for 所有边(a,b,w)
		dist[b]=min(dist[b],dist[a]+w);
  • 因为每次遍历所有边,所以用结构体存储所有边的起点、终点、权值就可以了
  • 因为存在负权边,所以注意判断条件
#include<bits/stdc++.h>
using namespace std;
const int maxn=510;
const int maxm=1e5+10;
const int INF=0x3F3F3F3F;

struct Edge{
    int x,y,w;
}edge[maxm];
int n,m,k;
int d[maxn],cp[maxn];
bool BellmanFord(int s){
    fill(d,d+maxn,INF);
    d[s]=0;

    for(int i=0;i<k;i++){
        memcpy(cp,d,sizeof d);  //防止一次走两步 作一个拷贝
        for(int j=0;j<m;j++){
            Edge t=edge[j];
            d[t.y]=min(d[t.y],cp[t.x]+t.w);
        }
    }

    if(d[n]>0x3f3f3f3f/2) return false; // 注意判断条件 
    else return true;
}


int main(){
    cin>>n>>m>>k;
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        edge[i]={a,b,c};
    }

    bool t=BellmanFord(1);
    if(!t) printf("impossible");
    else printf("%d\n",d[n]);

}

AcWing 851. spfa求最短路

  • 对BF算法进行优化,spfa是只有dist[a]变小,dist[b]才会变小。利用宽搜进行优化(队列)

  • 用bool数组标记b当前有无入队

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3F3F3F3F;

struct Node{
    int v,dis;
};

vector<Node> g[maxn];
int d[maxn];
int n,m;
bool st[maxn];//st是标志现在点i有没有在队列里

bool SPFA(int s){
    fill(d,d+maxn,INF);
    queue<int> q;
    q.push(s);
    d[s]=0;
    st[s]=true;

    while(!q.empty()){
        int top=q.front();
        q.pop();
        st[top]=false;

        for(int i=0;i<g[top].size();i++){
            int v=g[top][i].v;
            int dis=g[top][i].dis;
            if(d[v]>d[top]+dis){
                d[v]=d[top]+dis;
                if(!st[v]){
                    q.push(v);
                    st[v]=true;
                }
            }
        }
    }

    if(d[n]>0x3F3F3F3F/2) return false;
    else return true;
    
}


int main(){
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        Node node;
        node.v=b;
        node.dis=c;
        g[a].push_back(node);
    }


    bool f=SPFA(1);
    if(!f) puts("impossible");
    else printf("%d",d[n]);

}



AcWing 852. spfa判断负环

统计当前每个点的最短路中所包含的边数,如果某点的最短路所包含的边数大于等于n,则也说明存在环
新加一个数组 cnt[i] 表示走到这个i点所需要的步数,对应dist[i]

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3F3F3F3F;

struct Node{
    int v,dis;
};

vector<Node> g[maxn];
int d[maxn],cnt[maxn];
int n,m;
bool st[maxn];//st是标志现在点i有没有在队列里

bool SPFA(){ //返回有无负环
    //fill(d,d+maxn,INF);
    queue<int> q;
    for(int i=1;i<=n;i++){
        q.push(i);
        st[i]=true;
    }
    
    
    while(!q.empty()){
        int top=q.front();
        q.pop();
        st[top]=false;

        for(int i=0;i<g[top].size();i++){
            int v=g[top][i].v;
            int dis=g[top][i].dis;
            if(d[v]>d[top]+dis){
                d[v]=d[top]+dis;
                cnt[v]=cnt[top]+1; //前一个节点步数+1
                if(cnt[v]>=n) return true;//有负环
                if(!st[v]){
                    q.push(v);
                    st[v]=true;
                }
            }
        }
    }

    return false; //无负环
    
}


int main(){
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        Node node;
        node.v=b;
        node.dis=c;
        g[a].push_back(node);
    }
    bool f=SPFA();

    
    if(f) puts("Yes");
    else puts("No");

}



AcWing 854. Floyd求最短路

  • 注意重边
  • 注意初始化图的过程
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3F3F3F3F;
const int maxn=210;
const int maxm=2e5+10;
int n,m,k;
//int g[maxn][maxn]; //不需要g
int d[maxn][maxn];

void floyd(){

    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(d[i][k]!=INF&&d[k][j]!=INF&&d[i][j]>d[i][k]+d[k][j]){
                    d[i][j]=d[i][k]+d[k][j];
                }
            }
        }
    }

}

int main(){
    cin>>n>>m>>k;
 //   fill(g[0],g[0]+maxn*maxn,INF);
    fill(d[0],d[0]+maxn*maxn,INF);
    for(int i=1;i<=n;i++){
        d[i][i]=0;
    }
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        //注意重边
        d[a][b]=min(d[a][b],c);
        d[a][a]=0;d[b][b]=0; //不初始化 那么可能有自环
    }

    floyd();

    while(k--){
        int a,b;
        scanf("%d%d",&a,&b);
        if(d[a][b]>INF/2) printf("impossible\n"); //由于有负权边存在所以约大过INF/2也很合理
        else printf("%d\n",d[a][b]);
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值