最短路

我 讨 厌 静 电!!!


 最短路 - OI Wiki (oi-wiki.org)

最短路分类及时间复杂度


 

Floyd算法

是用来求任意两个结点之间的最短路的。

复杂度比较高,但是常数小,容易实现。(我会说只有三个 for 吗?)

适用于任何图,不管有向无向,边权正负,但是最短路必须存在。(不能有个负环)

三维的数组能够优化成二维数组进行计算

如图是三维数组思路及模板  暴力且无脑66

 1 #include <bits/stdc++.h>
 2 #define endl '\n'
 3 #define x first
 4 #define y second
 5 #define int long long
 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 7 
 8 using namespace std;
 9 
10 const int N=210;
11 const int INF=1e9;
12 const int mod=1e9+7;
13 
14 int n,m,k;
15 int dist[N][N];
16 
17 void Floyd()    //可以将三维Floyd的空间优化为二维进行计算
18 {
19     
20     for(int k=1;k<=n;k++)
21         for(int i=1;i<=n;i++)
22             for(int j=1;j<=n;j++)
23                 dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]); //循环结束后dist[i][j]存的就是i到j的最短距离
24     
25 }
26 
27 void solve()
28 {
29     
30     cin >> n >> m >> k;
31     
32     for(int i=1;i<=n;i++)
33         for(int j=1;j<=n;j++)
34             if(i==j)
35                 dist[i][j]=0;   //到自己的距离是0
36             else
37                 dist[i][j]=INF;
38     while(m--)
39     {
40         int a,b,c;
41         cin >> a >> b >> c;
42         dist[a][b]=min(dist[a][b],c);
43     }
44     
45     Floyd();
46     
47     while(k--)
48     {
49         
50         int a,b;
51         cin >> a >> b;
52         
53         if(dist[a][b]>INF/2)
54             cout << "impossible" << endl;
55         else
56             cout << dist[a][b] << endl;
57         
58     }
59     
60 }
61 
62 signed main()
63 {
64     Tang
65     int T=1;
66         //cin >> T;
67     while(T--)
68         solve();
69     
70     return 0;
71         
72 }

松弛操作:

 

 

 

 

 

 (上图中左到右的距离在一次松弛操作中由4更新为3)


 

Bellman_Ford算法

 

 

 1 #include <bits/stdc++.h>
 2 #define endl '\n'
 3 #define x first
 4 #define y second
 5 #define int long long
 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 7 
 8 using namespace std;
 9 
10 const int N=510,M=10010;
11 const int INF=0x3f3f3f3f;
12 const int mod=1e9+7;
13 
14 struct node 
15 {
16     int a,b,c;
17 }q[M];
18 
19 int n,m,k;
20 int dist[N];
21 int last[N];
22 
23 void Bellman_ford()
24 {
25     
26     memset(dist,0x3f,sizeof dist);
27     dist[1]=0;
28     for(int i=0;i<k;i++)
29     {
30         
31         memcpy(last,dist,sizeof dist);  //将dist的数值存到last中
32         for(int j=0;j<m;j++)
33         {
34             auto t=q[j];
35             dist[t.b]=min(dist[t.b],last[t.a]+t.c);     //进行松弛操作
36         }
37         
38     }
39     
40 }
41 
42 void solve()
43 {
44     
45     cin >> n >> m >> k;
46     
47     for(int i=0;i<m;i++)
48     {
49         
50         int a,b,c;
51         cin >> a >> b >> c;
52         q[i]={a,b,c};
53         
54     }
55     
56     Bellman_ford();
57     
58     if(dist[n]>INF/2)
59         cout << "impossible" << endl;
60     else
61         cout << dist[n] << endl;
62     
63 }
64 
65 signed main()
66 {
67     Tang
68     int T=1;
69         //cin >> T;
70     while(T--)
71         solve();
72     
73     return 0;
74         
75 }

SPFA算法

只有上一次被松弛的结点,所连接的边,才有可能引起下一次的松弛操作。

很多时候我们并不需要那么多无用的松弛操作。

 

 

 1 #include <bits/stdc++.h>
 2 #define endl '\n'
 3 #define x first
 4 #define y second
 5 #define int long long
 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 7 
 8 using namespace std;
 9 
10 const int N=1e5+10;
11 const int INF=0x3f3f3f3f;
12 const int mod=1e9+7;
13 
14 int n,m;
15 int h[N],e[N],ne[N],w[N],idx;
16 int dist[N];
17 bool st[N];
18 
19 void add(int a,int b,int c)
20 {
21     e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
22 }
23 
24 int Spfa()
25 {
26     
27     memset(dist,0x3f,sizeof dist);
28     dist[1]=0;
29     
30     queue<int> q;
31     q.push(1);
32     st[1]=true;
33     
34     while(q.size())
35     {
36         
37         int t=q.front();
38         q.pop();
39         
40         st[t]=false;
41         
42         for(int i=h[t];i!=-1;i=ne[i])
43         {
44             int j=e[i];
45             if(dist[j]>dist[t]+w[i])
46             {
47                 dist[j]=dist[t]+w[i];
48                 if(!st[j])
49                 {
50                     q.push(j);
51                     st[j]=true;
52                 }
53             }
54         }
55         
56     }
57     return dist[n];
58 }
59 
60 void solve()
61 {
62     
63     cin >> n >> m;
64     
65     memset(h,-1,sizeof h);
66     
67     while(m--)
68     {
69         
70         int a,b,c;
71         cin >> a >> b >> c;
72         add(a,b,c);
73         
74     }
75     
76     if(Spfa()>INF/2)
77         cout << "impossible" << endl;
78     else
79         cout << Spfa() << endl;
80     
81 }
82 
83 signed main()
84 {
85     Tang
86     int T=1;
87         //cin >> T;
88     while(T--)
89         solve();
90     
91     return 0;
92         
93 }

并常常用SPFA来判断负环是否存在

 1 #include <bits/stdc++.h>
 2 #define endl '\n'
 3 #define x first
 4 #define y second
 5 #define int long long
 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 7 
 8 using namespace std;
 9 
10 const int N=2010;
11 const int M=10010;
12 const int INF=0x3f3f3f3f;
13 const int mod=1e9+7;
14 
15 int n,m;
16 int h[N],e[M],ne[M],w[M],idx;
17 int dist[N],cnt[N];
18 bool st[N];
19 
20 void add(int a,int b,int c)
21 {
22     e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
23 }
24 
25 bool spfa()
26 {
27     
28     queue<int> q;
29     
30     for(int i=1;i<=n;i++)
31     {
32         st[i]=true;
33         q.push(i);
34     }
35     
36     while(q.size())
37     {
38         
39         int t=q.front();
40         q.pop();
41         
42         st[t]=false;
43         
44         for(int i=h[t];i!=-1;i=ne[i])
45         {
46             int k=e[i];
47             if(dist[k]>dist[t]+w[i])
48             {
49                 dist[k]=dist[t]+w[i];
50                 cnt[k]=cnt[t]+1;
51                 
52                 if(cnt[k]>=n)
53                     return true;
54                 if(!st[k])
55                 {
56                     q.push(k);
57                     st[k]=true;
58                 }
59             }
60         }
61         
62     }
63     return false;
64 }
65 
66 void solve()
67 {
68     
69     cin >> n >> m;
70     
71     memset(h,-1,sizeof h);
72     
73     while(m--)
74     {
75         
76         int a,b,c;
77         cin >> a >> b >> c;
78         add(a,b,c);
79         
80     }
81     
82     if(spfa())
83         cout << "Yes" << endl;
84     else
85         cout << "No" << endl;
86     
87 }
88 
89 signed main()
90 {
91     Tang
92     int T=1;
93         //cin >> T;
94     while(T--)
95         solve();
96     
97     return 0;
98         
99 }

Dijkstra算法

 

 

 

 1 #include <bits/stdc++.h>
 2 #define endl '\n'
 3 #define x first
 4 #define y second
 5 #define int long long
 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 7 
 8 using namespace std;
 9 
10 const int N=510;
11 const int INF=0x3f3f3f3f;
12 const int mod=1e9+7;
13 
14 int n,m;
15 int g[N][N];    //邻接矩阵储存所有边权
16 int dist[N];    //存储1到各个点的距离
17 bool st[N];     //标记,判断是否走过
18 
19 void Dijkstra()
20 {
21     
22     memset(dist,0x3f,sizeof dist);  //清空距离数组
23     dist[1]=0;
24     
25     for(int i=0;i<n-1;i++)
26     {
27         
28         int t=-1;
29         
30         for(int j=1;j<=n;j++)
31             if(!st[j] && (t==-1 || dist[t]>dist[j]))    //选取一个最短路的点放入dist数组中
32                 t=j;
33         for(int j=1;j<=n;j++)
34             dist[j]=min(dist[j],dist[t]+g[t][j]);   //将刚放入dist数组的点遍历进行松弛操作
35         
36         st[t]=true;
37         
38     }
39     if(dist[n]>0x3f3f3f3f/2)
40         cout << -1 << endl;
41     else
42         cout << dist[n] << endl;
43     
44 }
45 
46 void solve()
47 {
48     
49     cin >> n >> m;
50     memset(g,0x3f,sizeof g);
51     
52     for(int i=0;i<m;i++)
53     {
54         int a,b,c;
55         cin >> a >> b >> c;
56         
57         g[a][b]=min(g[a][b],c); //求最短路时只需要存储两点之间最短的一条边就可以
58     }
59     
60     Dijkstra();
61     
62 }
63 
64 signed main()
65 {
66     Tang
67     int T=1;
68         //cin >> T;
69     while(T--)
70         solve();
71     
72     return 0;
73         
74 }

 1 #include <bits/stdc++.h>
 2 #define endl '\n'
 3 #define x first
 4 #define y second
 5 #define int long long
 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 7 
 8 using namespace std;
 9 
10 const int N=1e6+10;
11 const int INF=0x3f3f3f3f;
12 const int mod=1e9+7;
13 
14 typedef pair<int,int> PII;
15 
16 int n,m;
17 int h[N],e[N],ne[N],w[N],idx;
18 int dist[N];
19 bool st[N];
20 
21 void add(int a,int b,int c) //数组模拟链表加入操作
22 {
23     e[idx]=b,ne[idx]=h[a],w[idx]=c;h[a]=idx++;
24 }
25 
26 int Dijkstra()
27 {
28     
29     memset(dist,0x3f,sizeof dist);
30     dist[1]=0;
31     priority_queue<PII,vector<PII>,greater<PII>> q; //建立小根堆
32     q.push({0,1});  //first是dist[i],second是节点i
33     
34     while(q.size()) //当队列不空
35     {
36         
37         auto t=q.top(); //每次取堆顶元素进行操作并将元素弹出
38         q.pop();
39         
40         int dis=t.first,ver=t.second;
41         
42         if(st[ver])
43             continue;
44         st[ver]=true;
45         
46         for(int i=h[ver];i!=-1;i=ne[i]) //进行松弛操作
47         {
48             int j=e[i];
49             if(dist[j]>dist[ver]+w[i])
50             {
51                 dist[j]=dist[ver]+w[i]; //更新该点的最短路的dist值
52                 q.push({dist[j],j});    //将新进入的最短路的点放入队列中
53             }
54             
55         }
56         
57     }
58     if(dist[n]>0x3f3f3f3f/2)
59         return -1;
60     else
61         return dist[n];
62     
63 }
64 
65 void solve()
66 {
67     
68     cin >> n >> m;
69     
70     memset(h,-1,sizeof h);
71     
72     while(m--)
73     {
74         
75         int a,b,c;
76         cin >> a >> b >> c;
77         add(a, b, c);
78         
79     }
80     
81     cout << Dijkstra() << endl;
82     
83 }
84 
85 signed main()
86 {
87     Tang
88     int T=1;
89         //cin >> T;
90     while(T--)
91         solve();
92     
93     return 0;
94         
95 }

 

posted @ 2022-03-02 11:40  MrSugarT  阅读(70)  评论(0编辑  收藏  举报