• 最短路模板


    Floyd()

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = ???;
    const int INF = 0x3f3f3f3f;
    int n, m;
    int a, b, x;
    int dis[N][N];
    
    void Floyd(){
        for(int k=0; k<n; k++)
            for(int i=0; i<n; i++)
                for(int j=0; j<n; j++)
                //选择从i到j的最短路 ( 比较当前知道的i到j的最短距离 与 从i到k再到j的距离) 
                    if(dis[i][j] > dis[i][k]+dis[k][j])
                        dis[i][j] = dis[i][k] + dis[k][j];
                    //dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);min比较慢 
    }
    
    int main()
    {
        while(scanf("%d %d",&n,&m)!=EOF){
            //初始化 
            for(int i=0; i<n; i++){
                for(int j=0; j<n; j++)
                    dis[i][j] = INF;
                dis[i][i] = 0;
            }
            //输入两点距离 
            for(int i=0; i<m; i++){
                scanf("%d %d %d", &a, &b, &x);
                x = min(dis[a][b], x);    
                dis[a][b] = dis[b][a] = x;
            }
            // 
            Floyd();
            // 
            int S,T;
            scanf("%d %d",&S,&T);
            if(dis[S][T]==INF)    printf("-1
    ");
            else                printf("%d
    ",dis[S][T]);
        }
        
    }
    View Code

    Bellman()

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<bits/stdc++.h>
     4 using namespace std;
     5 
     6 const int inf = 0x3f3f3f3f;
     7 const int num = ???
     8 struct edge{int u, v, w;}e[???];//边:起点u,终点v,权值(距离)w 
     9 int n, m, cnt;
    10 int dis[num];//从规定的起点到一点的距离 
    11 int pre[num];//记录前驱节点
    12             //eg:pre[x] = y,指从规定的起点到x点的最短路径为 起点->y->x 
    13 
    14 //打印从s到t的最短路径 
    15 void print_path(int s, int t){
    16     if(s == t) {printf("%d
    ",s); return;}
    17     print_path(s, pre[t]);
    18     printf("->%d",t);
    19 }
    20 
    21 //
    22 void bellman(){
    23     int s = 1;    //规定起点为1
    24     for(int i=1; i<=n; i++)
    25         dis[i] = inf;
    26     dis[s] = 0;
    27     
    28     for(int k=1; k<=n; k++)
    29         for(int i=0; i<cnt; i++){
    30             int x = e[i].u, y = e[i].v, z = e[i].w;
    31             if(dis[x] > dis[y] + z){
    32                 dis[x] = dis[y] + z;
    33                 pre[x] = y;            //如果经过y点到x点距离更短,则更新距离,并记录路径 
    34             } 
    35         }
    36     //print_path(s,?)        如果有需要,打印路径 
    37 }
    38 
    39 int main(){
    40     while(~scanf("%d %d", &n, &m) && n && m){
    41         cnt = 0;
    42         while(m--){
    43             int a, b, c;
    44             scanf("%d %d %d", &a, &b, &c);
    45             e[cnt].u = a;    e[cnt].v = b;    e[cnt].w = c;    cnt++;46             e[cnt].u = b;    e[cnt].v = a;    e[cnt].w = c;    cnt++;
    46         }
    47         bellman();
    48     } 
    49     return 0;
    50 }
    View Code

    Bellman()负圈判断

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<bits/stdc++.h>
     4 using namespace std;
     5 
     6 const int inf = 0x3f3f3f3f;
     7 const int num = ???
     8 struct edge{int u, v, w;}e[???];//边:起点u,终点v,权值(距离)w 
     9 int n, m, cnt;
    10 int dis[num];//从规定的起点到一点的距离 
    11 int pre[num];//记录前驱节点
    12             //eg:pre[x] = y,指从规定的起点到x点的最短路径为 起点->y->x 
    13 
    14 //打印从s到t的最短路径 
    15 void print_path(int s, int t){
    16     if(s == t) {printf("%d
    ",s); return;}
    17     print_path(s, pre[t]);
    18     printf("->%d",t);
    19 }
    20 
    21 //
    22 void bellman(){
    23     int s = 1;    //规定起点为1
    24     for(int i=1; i<=n; i++)
    25         dis[i] = inf;
    26     dis[s] = 0;
    27     int k = 0;            //记录有几轮操作
    28     bool update = true;    //判断是否有更新 
    29     
    30     while(update){
    31         k++;
    32         update = false;
    33         if(k > n){printf("有负圈");return ;}    //有负圈,停止
    34         for(int i=0; i<cnt; i++){
    35             int x = e[i].u, y = e[i].v;
    36             if(d[x] > d[y] + e[i].w){
    37                 update = true;
    38                 d[x] = d[y] + e[i].w;
    39                 pre[x] = y;
    40             }
    41         } 
    42     }
    43     
    44     //print_path(s,?);
    45 }
    46 
    47 int main(){
    48     while(~scanf("%d %d", &n, &m) && n && m){
    49         cnt = 0;
    50         while(m--){
    51             int a, b, c;
    52             scanf("%d %d %d", &a, &b, &c);
    53             e[cnt].u = a;    e[cnt].v = b;    e[cnt].w = c;    cnt++:
    54             e[cnt].u = b;    e[cnt].v = a;    e[cnt].w = c;    cnt++;
    55         }
    56         bellman();
    57     } 
    58     return 0;
    59 }
    View Code

    SPFA()。SPFA是用队列处理Bellman-Ford算法,效率很高。但他并不稳定。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int inf = 0x3f3f3f3f;
     5 const int num = ???;
     6 struct edge{
     7     int from, to, w;//边:起点from(其实没有用到),终点to,权值(距离)w
     8     edge(int a, int b, int c){from=a; to=b; w=c;} 
     9 };
    10 vector<edge>e[num];//第i个节点连接的所有边 
    11 int n, m;
    12 int dis[num];//从规定起点到点i的距离 
    13 int pre[num];//记录路径 
    14 
    15 //打印路径 
    16 void print_path(int s, int t){
    17     if(s == t){printf("%d",s);    return ;}
    18     print_path(s, pre[t]);
    19     printf("->%d",t); 
    20 }
    21 
    22 int spfa(int s){
    23     bool inq[num];//标记节点i是否在队列中
    24     int neg[num];//判断负圈 
    25     //初始化 
    26     for(int i=1; i<=n; i++){
    27         neg[i] = 0;
    28         dis[i] = inf;
    29         inq[i] = false;
    30     } 
    31     neg[s] = 1;
    32     //
    33     queue<int>q;
    34     q.push(s);
    35     inq[s] = true;//s进队 
    36     //
    37     while(!q.empty()){
    38         int u = q.front();
    39         q.pop();
    40         inq[u] = false;//s出队
    41         
    42         for(int i=0; i<e[u].size(); i++){//检查节点u连接的所有边 
    43             int v = e[u][i].to, w = e[u][i].w;
    44             if(dis[u] + w < dis[v]){//起点->u->v的距离 < 起点->v的距离 
    45                 dis[v] = dis[u] + w;
    46                 pre[v] = u;
    47                 if(!inq[v]){//如果v不在队列则插入 
    48                     inq[v] = true;
    49                     q.push(v);
    50                     neg[v]++;
    51                     if(neg[v] > n)    return 1;//v的更新次数超过节点个数,则说明存在负圈 
    52                 }
    53             }
    54         } 
    55     }
    56     //print_path(s,?);
    57     return 0;
    58 }
    59 
    60 int main(){
    61     while(~scanf("%d %d", &n, &m) && n && m){
    62         for(int i=1; i<=n; i++)        e[i].clear();
    63         while(m--){
    64             int a, b, c;
    65             scanf("%d %d %d", &a, &b, &c);
    66             e[a].push_back(edge(a,b,c));
    67             e[b].push_back(edge(b,a,c));
    68         }
    69         spfa(1);//规定1为起点 
    70     } 
    71     return 0;
    72 }
    View Code

    SPFA()-链式前向星。极端情况下,图特别大,用邻接链表也会超空间限制,此时需要用到链式前向星来存图。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int inf = INT_MAX / 10;
     5 const int num = ???;
     6 struct Edge{
     7     int to, next, w;//edge[i]的i就是起点,终点to,权值w,相同起点的下一条边next 
     8 }edge[num];
     9 int n, m, cnt;
    10 int head[num];//head[i]存放以i为起点的下一条边
    11 int dis[num];
    12 int inq[num];//标记节点是否在队列中
    13 int neg[num];//判负圈
    14 int pre[num];//记录前驱节点
    15 
    16 void print_path(int s, int t){
    17     if(s==t)    {printf("%d",s);    return ;}
    18     print_path(s, pre[t]);
    19     printf("->%d",t);
    20 }
    21 
    22 //初始化 
    23 void init(){
    24     for(int i=0; i<num; ++i){
    25         edge[i].next = -1;
    26         head[i] = -1;
    27     }
    28     cnt = 0;
    29 }
    30 
    31 //加边 
    32 void addedge(int u, int v, int w){
    33     edge[cnt].to = v;
    34     edge[cnt].w  = w;
    35     edge[cnt].next = head[u];//指向上一条同起点的边 
    36     head[u] = cnt;//存放以u为起点的某条边edge的下标 
    37     cnt++;
    38 }
    39 
    40 int spfa(int s){
    41     for(int i=1; i<=n; ++i){
    42         neg[i] = 0;
    43         dis[i] = inf;
    44         inq[i] = false;
    45     }
    46     neg[s] = 1;
    47     dis[s] = 0;
    48     
    49     queue<int>q;
    50     q.push(s);
    51     inq[s] = true;
    52     
    53     while(!q.empty()){
    54         int u = q.front();
    55         q.pop();
    56         inq[u] = false;
    57         
    58         for(int i=head[u]; ~i; i=edge[i].next){//~1 即 i!=-1 
    59             int v = edge[i].to;
    60             int w = edge[i].w;
    61             if(dis[u] + w < dis[v]){
    62                 dis[v] = dis[u] + w;
    63                 pre[v] = u;
    64                 if(!inq[v]){
    65                     inq[v] = true;
    66                     q.push(v);
    67                     neg[v]++;
    68                     if(neg[v] > n) return 1;//v的更新次数超过节点个数n,即说明存在负圈 
    69                 }
    70             }
    71         }
    72     }
    73     //print_path(s,?);
    74     return 0;
    75 }
    76 
    77 int main(){
    78     while(~scanf("%d %d", &n, &m) && n && m){
    79         //初始化 
    80         init();
    81         //
    82         while(m--){
    83             int a, b, c;
    84             scanf("%d %d %d", &a, &b, &c);
    85             addedge(a, b, c);
    86             addedge(b, a, c);
    87         }
    88         //
    89         spfa(1);
    90     }
    91     return 0;
    92 }
    View Code

    dijkstra()

     1 #include<bits/stdc++.h>
     2 #include<vector>
     3 #include<queue>
     4 using namespace std;
     5 
     6 const int inf = 0x3f3f3f3f;
     7 const int num = ???;
     8 struct edge{
     9     int from, to, w;
    10     edge(int a, int b, int c){from=a; to=b; w=c;}
    11 };
    12 vector<edge>e[num];
    13 struct node{
    14     int id, n_dis;//id节点,n_dis节点到起点的距离
    15     node(int b, int c){id=b; n_dis=c;}
    16     bool operator <(const node &a)const
    17     {    return n_dis > a.n_dis;}
    18 };
    19 int n, m, s;
    20 int pre[num];
    21 int dis[num];
    22 bool done[num];
    23 
    24 void print_path(int s, int t){
    25     if(s==t){printf("%d",s); return ;}
    26     print_path(s, pre[t]);
    27     printf("->%d",t);
    28 }
    29 
    30 void dijkstra(){
    31     //
    32     s=  1;
    33     for(int i=1; i<=n; ++i){
    34         dis[i] = inf;
    35         done[i] = false;
    36     }
    37     dis[s] = 0;
    38     //
    39     priority_queue<node>q;
    40     q.push(node(s, dis[s]));
    41     //
    42     while(!q.empty()){
    43         node u = q.top();
    44         q.pop();
    45         //
    46         if(done[u.id])    continue;
    47         //
    48         done[u.id] = true;
    49         for(int i=0; i<e[u.id].size(); ++i){
    50             edge y = e[u.id][i];
    51             //
    52             if(done[y.to])    continue;
    53             //
    54             if(dis[y.to] > y.w + u.n_dis){
    55                 dis[y.to] = y.w + u.n_dis;
    56                 q.push(node(y.to, dis[y.to]));
    57                 pre[y.to] = u.id;
    58             }
    59         }
    60     }
    61     //print_path(s,?);
    62 }
    63 
    64 int main(){
    65     while(~scanf("%d %d", &n, &m) && n && m){
    66         //
    67         for(int i=1; i<=n; ++i)        e[i].clear();
    68         //
    69         while(m--){
    70             int a, b, c;
    71             scanf("%d %d %d", &a, &b, &c);
    72             e[a].push_back(edge(a, b, c));
    73             e[b].push_back(edge(b, a, c));
    74         }
    75         //
    76         dijkstra();
    77     }
    78     return 0;
    79 }
    View Code

      1.0  Floyd(),简单粗暴,三重循环遍历,可求多源两点距离,但毕竟复杂度高,不适合过大的图。

      2.0  Bellman(),借用一个(存放起点u、终点v、权值w的)结构体edge,遍历次数取决于边的个数*节点个数n,将复杂的三重循环,缩小到O(nm)的复杂度。

      2.1  在Bellman()的基础上增设一个标记值k,用于判断负圈。

      3.0  SPFA(),在Bellman的基础上,增加了一个vector<edge>e[num],将同起点的边归类,再次减小复杂度。同时在函数中用到了队列,类似于BFS,取出队首的节点以后,遍历以该节点为起点的所有边,然后依次将终点插入队尾。

      3.1  SPFA()链式前向星,原模板仍有缺陷,遇到特别大的图时,用邻接表也会超空间限制,此时就需要用链式前向星来存图。摒弃了上一模板使用的vector,重用结构体数组,并在结构体中增设next指向同起点的下一条边,增设了数组head[],用联系同一起点的前后两条边。同时增设addedge函数应对复杂起来的加边程序。链式前向星是用来应对图过大,节点过多的情况,减小空间复杂度。

      4.0  dijkstra(),大体是在原版的SPFA的基础上,改用了优先队列priority_queue,能够更快地找到某个节点的最短路径, 以减小时间复杂度。

  • 相关阅读:
    psp4
    一维循环数组最大子数组求解
    psp3
    psp2
    求二维数组最大连续子数组
    计算连续最大子数组问题
    psp1
    关于30道题目问题
    Java 文件操作
    java iterator
  • 原文地址:https://www.cnblogs.com/0424lrn/p/12262862.html
Copyright © 2020-2023  润新知