最短路:Dijkstra Spfa BellmanFord Floyd(C++和JAVA)

Dijkstra算法解释在下面,点目录可以直接跳转。

先提供三种算法的基础模板:

  • 1.Floyd
//floyd
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1007;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
int dis[N][N];
int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        memset(dis,0x3f,sizeof(dis));
        for(int i=0,u,v,w;i<m;++i){
            scanf("%d%d%d",&u,&v,&w);
            dis[u][v]=min(dis[u][v],w);
        }
        for(int k=1;k<=n;++k){
            for(int i=1;i<=n;++i){
                for(int j=1;j<=n;++j){
                    dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
                }
            }
        }
        for(int i=2;i<=n;++i){
            printf("1点到%d点的最短路径:%d\n",i,dis[1][i] );
        }
    }
    return 0;
}
/*
4 8
1 2 2
1 3 6
1 4 4
2 3 3
3 1 7
3 4 1
4 1 5
4 3 12
*/

  • 2.dijkstra:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<assert.h>
#include<bitset>
#define lson rt<<1
#define rson rt<<1|1
#define lsonl l,mid,rt<<1
#define rsonr mid+1,r,rt<<1|1
#define lowbit(x) (x)&(-(x))
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int INF = 0x3f3f3f3f;
const int N = (int)1e5 +107;
struct lp{
  int v, w;
  lp(){}
  lp(int a,int b){v=a;w=b;}
  friend bool operator <(const lp &a,const lp &b){
    return a.w>b.w;
  }
}cw[N];
int n, m;
int dis[N], vis[N];
vector<pii> mp[N];
void dij(){
  priority_queue<lp> Q;
  Q.push(lp(n,0));
  dis[n]=0;
  while(!Q.empty()){
    lp b = Q.top();Q.pop();
    if(vis[b.v])continue;
    vis[b.v]=1;
    int len = mp[b.v].size();
    for(int i=0;i<len;++i){
      int v = mp[b.v][i].first,w=mp[b.v][i].second;
      if(vis[v])continue;
      if(dis[v]>dis[b.v]+w){
        dis[v]=dis[b.v]+w;
        Q.push(lp(v,dis[v]));
      }
    }
  }
  printf("%d\n", dis[1]);
}
inline void init(){
  for(int i=1;i<=n;++i)mp[i].clear();
  memset(dis,0x3f,sizeof(dis));
  memset(vis,0,sizeof(vis));
}
int main(){
  while(~scanf("%d%d", &m, &n)){
    init();
    for(int i=0,u,v,w;i<m;++i){
      scanf("%d%d%d", &u, &v, &w);
      mp[u].push_back(make_pair(v,w));
      mp[v].push_back(make_pair(u,w));
    }
    dij();
  }
  return 0;
}
  • 3.spfa:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1007;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
int tot, dist[N], head[N],vis[N];
int n, m, w;
struct Edge {
    int v, w, nex;
} cw[N];
void add(int u, int v, int w){
    cw[++tot].v=v;cw[tot].w=w;cw[tot].nex=head[u];head[u]=tot;
}
void spfa(){
    queue<int>Q;
    while(!Q.empty())Q.pop();
    memset(dist,0x3f,sizeof(dist));
    memset(vis,0,sizeof(vis));
    dist[1]=0;
    Q.push(1);
    while(!Q.empty()){
        int p=Q.front();Q.pop();
        vis[p]=0;
        for(int i=head[p];i!=-1;i=cw[i].nex){
            int q=cw[i].v;
            if(dist[q]>dist[p]+cw[i].w){
                dist[q]=dist[p]+cw[i].w;
                if(!vis[q]){
                    vis[q]=1;
                    Q.push(q);
                }
            }
        }
    }
}
int main(){
    int a, b, c;
    while(~scanf("%d%d", &n, &m)) {
        tot = -1;
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d", &a, &b, &c);
            add(a, b, c);
        }
        spfa();
        for(int i=2;i<=n;++i){
            printf("1点到%d点的最短路径:%d\n",i,dist[i] );
        }
    }
    return 0;
}

迪杰斯特拉算法:

算法讲解:

这里写图片描述
dijkstra是用来求单源最短路径的。算法结束后dis[i]保存的是起点 s 到 i 点的最短路径。下面简单讲一下算法过程。

  1. vis数组记录该点是否松弛过,可以理解为是否访问过。初始vis[1]=1,其他为0。可以证明的是算法最多松弛n-1次。反正可以证明。
  2. ar[][]用来存图。初始化为INF,如果a和b有一条权值为c的有向边,则ar[a][b]=c;;如果是无向边就是ar[a][b]=ar[b][a]=c;
  3. dis数组用来松弛,并且存最后起点到各个点的最短距离。
  4. 松弛一条边(u,v)的意思是能否从u点找到一个中介点p使得ar[u][p]+ar[p][v] < ar[u][v]。松弛完后,得到u到v的一条更短的路径。将图中除起点外的n-1个点松弛后,就可以得出起点到每个店的最短路了。

1.以1为起点做栗子,初始时点1只和点2和3相连,所以dis[1]=0;dis[2]=2;dis[3]=12;其他赋值为INF无穷大。
2.第二步类似与讲解prim算法的那一步,找出离起点最近的一个没有被访问过的点p。第一步的时候距离最近的是dis[2]=2。所以p=2;p点将被用来松弛,还有记得标记vis[p]=1;
3. 得到p点后遍历每个顶点,看顶点 i 是否通过这个中介点p 能 得出一条从起点s到点 i 的更短距离。比如说:第一轮的时候vis[3]还是0,且dis[3]=12;dis[p]+ar[p][3]=5;5小于12,非常棒所以dis[3]更新为5,还有vis[4]=0,且dis[4]=INF,dis[p]+ar[p][4]=8;8 < INF,所以dis[4]更新为8。然后vis[5]=vis[6]=0,dis[5]=dis[6]=INF,dis[p]+ar[p][5]=dis[p]+ar[p][6]=INF+2。这两个点就不用更新。
4. 重复第二步,共最多进行n-1次。因为n-1次后每个点应该都被标记了。前提是图联通,图不连通另说。

就这样讲完了,这时候再去上面看看上面的dij的代码,应该就感觉很好理解了。


下面附上一道例题:
题目链接:hdu1548

AC代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N=205;
const int INF=0x3f3f3f3f;
int n;
int ar[N][N],k[N],dis[N];
bool vis[N];
void dijkstra(int x,int y){
    for(int i=1;i<=n;i++){//初始化
        dis[i]=ar[x][i];//dis[i]保存的是起点到i点的最短距离
        vis[i]=0;
    }
    dis[x]=0;
    vis[x]=1;
    for(int i=1;i<n;i++){
        int mi=INF,p=0;
        for(int j=1;j<=n;j++){
            if(mi>dis[j]&&!vis[j]){
                mi=dis[j];
                p=j;
            }
        }
        if(mi==INF)break;
        vis[p]=1;
        for(int j=1;j<=n;++j){
            if(!vis[j]){
                dis[j]=min(dis[j],dis[p]+ar[p][j]);
            }
        }
    }
}
int main(){
    while(~scanf("%d",&n)&&n){
        int s,e;
        scanf("%d%d",&s,&e);
        for(int i=0;i<=n;i++){
            for(int j=0;j<=n;j++){
                ar[i][j]=INF;
            }
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&k[i]);
            if(i+k[i]<=n){
                ar[i][i+k[i]]=1;
            }
            if(i-k[i]>=1){
                ar[i][i-k[i]]=1;
            }
        }
        dijkstra(s,e);
        printf("%d\n",dis[e]==INF?-1:dis[e]);
    }
    return 0;
}

优先队列优化后的dijkstra算法:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<string>
#include<cmath>
using namespace std;
typedef long long LL;
const int N = 1010;
const int INF = 0x3f3f3f3f;
const LL mod = 10000;
const double eps = 1e-8;
struct lp{
    int to,w;
    lp(int a,int b){to=a;w=b;}
    bool operator <(const lp &a)const {
        if(w!=a.w) return w>a.w;
        return to<a.to;
    }
};
vector<lp>mp[N];
int dis[N],vis[N],s[N],d[N];
int tot,t,S,D;
void dij(int s){
    priority_queue<lp>Q;
    Q.push(lp(s,0));
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    while(!Q.empty()){
        lp x=Q.top();Q.pop();
        for(int i=0;i<mp[x.to].size();++i){
            lp t=mp[x.to][i];
            if(dis[t.to]>x.w+t.w){
                dis[t.to]=x.w+t.w;
                Q.push(lp(t.to,dis[t.to]));
            }
        }
    }
}
int main(){
    while(~scanf("%d%d%d",&t,&S,&D)){
        for(int i=0;i<N;++i)mp[i].clear();
        int a,b,c;
        for(int i=0;i<t;++i){
            scanf("%d%d%d",&a,&b,&c);
            mp[a].push_back(lp(b,c));
            mp[b].push_back(lp(a,c));
        }
        for(int i=0;i<S;++i){
            scanf("%d",&s[i]);
        }
        for(int i=0;i<D;++i){
            scanf("%d",&d[i]);
        }
        int mmin=INF;
        for(int i=0;i<S;++i){
            dij(s[i]);
            for(int j=0;j<D;++j){
                mmin=min(mmin,dis[d[j]]);
            }
        }
        printf("%d\n",mmin);
    }
    return 0;
}

spfa判断负环:

POJ 3259

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<string>
#include<cmath>
#define test printf("***\n")
#define ka getchar();getchar()
#define ka1 getchar()
#define iis std::ios::sync_with_stdio(false)
using namespace std;
typedef long long LL;
const int N = 5210;
const int INF = 0x3f3f3f3f;
const LL mod = 1000000007;
const double eps = 1e-8;
int tot, dist[N], head[N],vis[N];
int num[N];
int n, m, w;
struct Edge {
    int v, w, nex;
} cw[N];
void add(int u, int v, int w){
    cw[++tot].v=v;cw[tot].w=w;cw[tot].nex=head[u];head[u]=tot;
}
bool spfa(){
    queue<int>Q;
    while(!Q.empty())Q.pop();
    memset(dist,0x3f,sizeof(dist));
    memset(vis,0,sizeof(vis));
    memset(num,0,sizeof(num));
    dist[1]=0;
    num[1]++;//如果一个点进入队列的次数大于n次则存在负环
    Q.push(1);
    while(!Q.empty()){
        int p=Q.front();Q.pop();
        vis[p]=0;
        for(int i=head[p];i!=-1;i=cw[i].nex){
            int q=cw[i].v;
            if(dist[q]>dist[p]+cw[i].w){
                dist[q]=dist[p]+cw[i].w;
                if(!vis[q]){
                    vis[q]=1;
                    num[q]++;
                    Q.push(q);
                    if(num[q]>=n){
                        return true;
                    }
                }
            }
        }
    }
    return false;
}
int main() {
    int T, a, b, c;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d%d", &n, &m, &w);
        tot = 1;
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d", &a, &b, &c);
            add(a, b, c);
            add(b, a, c);
        }
        for(int i=1;i<=w;++i){
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,-c);
        }
        if(spfa() )printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

本题bellmanFord写法

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5210;
const int INF = 0x3f3f3f3f;
const LL mod = 1000000007;
const double eps = 1e-8;
int dist[N],head[N],tot;
int n,m,w;
struct lp{
    int u,v,w,nex;
}cw[N];
void add(int x,int y,int z){
    cw[++tot].v=y;cw[tot].u=x;cw[tot].w=z;cw[tot].nex=head[x];head[x]=tot;
}
bool bellmanFord(){
    memset(dist,0x3f,sizeof(dist));
    dist[1]=0;
    for(int i=0;i<n;++i){
        for(int j=2;j<=tot;++j){
            int v=cw[j].v,u=cw[j].u,w=cw[j].w;
            if(dist[v]>dist[u]+w){
                dist[v]=dist[u]+w;
            }
        }
    }
    for(int i=2;i<=tot;++i){
        if(dist[cw[i].v]>dist[cw[i].u]+cw[i].w){
            return 1;
        }
    }
    return 0;
}
int main(){
    int t,a,b,c;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&m,&w);
        tot=1;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=m;++i){
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
            add(b,a,c);
        }
        for(int i=1;i<=w;++i){
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,-c);
        }
        if(bellmanFord() )printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
JAVA实现

例题:传送门

70分代码:

import java.beans.IntrospectionException;
import java.io.*;
import java.lang.reflect.Array;
import java.math.*;
import java.util.*;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.text.DecimalFormat;
import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;


public class Main{
    static PrintStream p = System.out;
    static int mod = (int)1e9+7;
    static int N = 10005;
    static long INF = (long)1e17;
    class lp{
        public int u,v,nex;
        public long w;
        lp(int a,int b,long c,int d){
            this.u=a;this.v=b;this.w=c;this.nex=d;
        }
    }
    public int tot;
    public lp[] cw;
    public int[] head;
    public int[] vis;
    public long[] dis;
    public boolean spfa(int n){
        Queue<Integer>Q = new PriorityQueue<Integer>();
        int[] num = new int[n+1];
        Arrays.fill(num,0);
        Arrays.fill(vis,0);
        for(int i=0;i<=n;++i)dis[i]=INF;
        dis[1]=0;
        num[1]++;
        Q.add(1);
        while (!Q.isEmpty()){
            Integer u = Q.poll();
            vis[u]=0;
            for(int i=head[u];i!=-1;i=cw[i].nex){
                int v=cw[i].v;
                long w=cw[i].w;
                if(dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    if(vis[v]==1)continue;
                    num[v]++;
                    vis[v]=1;
                    Q.add(v);
                    if(num[v]>n)return false;
                }
            }
        }
        for(int i=2;i<=n;++i){
            p.println(dis[i]);
        }
        return true;
    }
    public void add(int x,int y,long z){
        cw[++tot]=new lp(x,y,z,head[x]);
        head[x]=tot;
        /*cw[++tot]=new lp(y,x,z,head[y]);
        head[y]=tot;*/
    }
    public static void main(String []args){
        Scanner cin = new Scanner(System.in);
        Main mai = new Main();;
        while (cin.hasNext()) {
            int n = cin.nextInt();
            int m = cin.nextInt();
            mai.cw = new lp[m+10];
            mai.head = new int[n+1];
            mai.dis = new long[n+1];
            mai.vis = new int[n+1];
            Arrays.fill(mai.head,-1);
            mai.tot = 1;
            for (int i = 0; i < m; ++i) {
                int x = cin.nextInt();
                int y = cin.nextInt();
                long z = cin.nextLong();
                mai.add(x, y,z);
            }
            boolean flag = mai.spfa(n);
     
        }
       
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值