码迷,mamicode.com
首页 > 其他好文 > 详细

最短路模板

时间:2020-02-05 11:44:17      阅读:47      评论:0      收藏:0      [点我收藏+]

标签:event   选择   const   需要   bit   queue   floyd   节点   tor   

Floyd()

技术图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = ???;
 5 const int INF = 0x3f3f3f3f;
 6 int n, m;
 7 int a, b, x;
 8 int dis[N][N];
 9 
10 void Floyd(){
11     for(int k=0; k<n; k++)
12         for(int i=0; i<n; i++)
13             for(int j=0; j<n; j++)
14             //选择从i到j的最短路 ( 比较当前知道的i到j的最短距离 与 从i到k再到j的距离) 
15                 if(dis[i][j] < dis[i][k]+dis[k][j])
16                     dis[i][j] = dis[i][k] + dis[k][j];
17                 //dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);min比较慢 
18 }
19 
20 int main()
21 {
22     while(scanf("%d %d",&n,&m)!=EOF){
23         //初始化 
24         for(int i=0; i<n; i++){
25             for(int j=0; j<n; j++)
26                 dis[i][j] = INF;
27             dis[i][j] = 0;
28         }
29         //输入两点距离 
30         for(int i=0; i<m; i++){
31             scanf("%d %d %d", &a, &b, &x);
32             x = min(dis[a][b], x);    
33             dis[a][b] = dis[b][a] = x;
34         }
35         // 
36         Floyd();
37         // 
38         int S,T;
39         scanf("%d %d",&S,&T);
40         if(dis[S][T]==INF)    printf("-1\n");
41         else                printf("%d\n",dis[S][T]);
42     }
43     
44 }
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\n",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

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()的基础上增设一个标记数组neg[],用于判断负圈。

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

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

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

最短路模板

标签:event   选择   const   需要   bit   queue   floyd   节点   tor   

原文地址:https://www.cnblogs.com/0424lrn/p/12262862.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!