音无结弦之时,天使跃动之心。立于浮华之世,奏响天籁之音。.|

次林梦叶

园龄:3年3个月粉丝:22关注:3

最短路总结

《好博客》

链接:https://blog.csdn.net/m0_50564748/article/details/123143604

最全的最短路问题综合

《Dijkstra总结》

《为什么Dijlstra不能用在有负权边的图中》

Dijkstra算法其只能用在dist不断变大(或不断变小,即要单调)的情况下;

这是由于算法本身的思想决定的:

 

 

 

 

 

 《变式(来自大佬的博客,我只是添加上我认为的解释)》

 

 

 

 由于dist[]对于未确定最小距离的点(还未加入S集合,还在V-S集合中)来说,一直是不断增大的,

能够dist[j]>dist[t]+g[t][j] 或 dist[j]==dist[t]+g[t][j],这个j一定是未确定最小距离的点

 

 

————————————————
版权声明:本文为CSDN博主「Cyber_Wz」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_50564748/article/details/123143604

《其中的一些小问题》

在上面有特殊条件的dijkstra如果不用heap的方式写,那么就不能够用首先for (int i=1;i<=n;i++) dist[i]=g[1][i]这样的写法了

因为如果这样,那么就在源点1处的特殊状态就不能得到更新

所以这个时候就只能用最简朴的,如这样的方法了:

 

 

 

复制代码
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <queue>
 5 #include <vector>
 6 using namespace std;
 7 typedef pair<int, int> PII;
 8 const int N = 1e5 + 1, mod = 100003;
 9 int n, m;
10 int dist[N];
11 bool st[N];
12 int cnt[N];
13 priority_queue<PII, vector<PII>, greater<PII>> heap;
14 vector<PII> g[N];
15 void dijkstra(int x)
16 {
17     memset(dist, 0x3f, sizeof(dist));
18     dist[x] = 0;
19     cnt[x] = 1;
20     heap.push({0, x});
21 
22     while (heap.size())
23     {
24         auto t = heap.top();
25         heap.pop();
26         int v = t.second;
27         /* cout<<"^^^"<<v<<endl; */
28         if (st[v])
29             continue;
30         st[v] = true;
31         /*  cout << "fa: " << v << " " << endl; */
32         for (int i = 0; i < g[v].size(); i++)
33         {
34             int child = g[v][i].second, w = g[v][i].first;
35             /*  cout << "Nchild: " << child << " "; */
36             if (!st[child])
37             {
38                 /*   cout << "child: " << child << endl;
39                   cout << dist[child] << " " << dist[v] << " " << w << endl; */
40                 if (dist[child] > dist[v] + w)
41                 {
42                     dist[child] = dist[v] + w;
43                     cnt[child] = cnt[v];
44                     heap.push({dist[child], child});
45                 }
46                 else if (dist[child] == dist[v] + w)
47                 {
48                     cnt[child] = (cnt[child] + cnt[v]) % mod;
49                 }
50             }
51         }
52     }
53 }
54 int main()
55 {
56     scanf("%d%d", &n, &m);
57     for (int i = 1; i <= m; i++)
58     {
59         int a, b;
60         scanf("%d%d", &a, &b);
61         g[a].push_back({1, b}), g[b].push_back({1, a});
62     }
63     dijkstra(1);
64     for (int i = 1; i <= n; i++)
65         cout << cnt[i] << endl;
66     return 0;
67 }
复制代码

 《状态转移在最短路中的应用(bfs,双端队列的使用其实是特殊的dijkstra)》

 

 

 

 

 我们发现如果没有这些门与钥匙的话,就是个普通的bfs或dijkstra

但是这里有,如果按照普通的bfs我们完全不知道当到达门时我们是否拿到了钥匙

则我们要多存储一项数据

dist[x][y][state]:表示从(1,1)到(x,y)时,有的钥匙状态为state的最短路径

state是以二进制保存钥匙状态,比如:有钥匙类型1,则state二进制状态下为000000001(因为钥匙类型最多有10种,最多10位)

状态转移:

 

 

则可以知道这个状态的转移的权重只有0,1,可以用双端队列写 

具体题目:https://www.acwing.com/problem/content/1133/

在最短路中的次短路问题

 

 

 

 这里同样如果只按照最原始的dijkstra算法是数据不够的

所以我们可以在开一维记录次短路的路径

dist[i][0]:表示从初始点到i点的最短路

dist[i][1]:表示从初始点到i点的次短路

cnt[i][0]:表示从初始点到i点的最短路的条数

cnt[i][1]:表示从初始点到i点的次短路的条数

在做dijkstra时可以进一个点看做两个点

因为一个点有两个状态--从最短路更新过来,从次短路更新过来

最短路只能根据最短路更新过来,而次短路可以根据最短路和次短路更新过来

 

 

 记住要将次短路加入优先队列,因为次短路更新要依据最短路和次短路更新过来

同时次短路也要确认为这是最短的次短路了,则st[N][2]应该是这样的

st[i][1]=true,表示从初始点到i点的次短路已经确定

复制代码
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <queue>
 5 #include <vector>
 6 using namespace std;
 7 const int N = 1001, INF = 0x3f3f3f3f;
 8 typedef pair<int, int> PII;
 9 int t, n, m, s, f;
10 int dist[N][2], cnt[N][2];
11 int g[N][N];
12 bool st[N][2];
13 struct Node
14 {
15     int v, type, w;
16     bool operator>(const Node &node) const
17     {
18         return w > node.w;
19     }
20 };
21 void dijkstra(int start, int final, vector<PII> g[])
22 {
23     memset(dist, 0x3f, sizeof(dist));
24     memset(cnt, 0, sizeof(cnt));
25     memset(st, false, sizeof(st));
26     priority_queue<Node, vector<Node>, greater<Node>> heap;
27     heap.push({start, 0, 0});
28     dist[start][0] = 0, cnt[start][0] = 1;
29     while (heap.size())
30     {
31         auto t = heap.top();
32         heap.pop();
33         int v = t.v, type = t.type, distance = t.w;
34        /*  cout<<"~~~"<<v<<" "<<type<<" "<<distance<<endl; */
35         if (st[v][type])
36             continue;
37         st[v][type] = true;
38         for (int i = 0; i < g[v].size(); i++)
39         {
40             int w = g[v][i].second, j = g[v][i].first;
41             /*cout << v << " " << j << " " << w << endl;*/
42             /* if (j == 5)
43             {
44                 cout << "!!" << v << " " << type << endl;
45                 cout << "!!" << dist[j][0] << " " << dist[v][type] + w << endl;
46                 cout << "!!" << dist[j][1] << " " << dist[v][type] + w << endl;
47             } */
48             if (dist[j][0] > distance + w)
49             {
50                 dist[j][1] = dist[j][0];
51                 cnt[j][1] = cnt[j][0];
52               /*   cout << j << " " << 1 << " " << cnt[j][1] << endl; */
53                /*  cout << "@@" << dist[j][1] << endl; */
54                 heap.push({j, 1, dist[j][1]});
55                 dist[j][0] = distance + w;
56                 cnt[j][0] = cnt[v][type];
57                /* cout << j << " " << 0 << " " << cnt[j][0] << endl;*/
58                 heap.push({j, 0, dist[j][0]});
59                 /* cout << "@@" << dist[j][0] << endl; */
60             }
61             else if (dist[j][0] == dist[v][type] + w)
62                 cnt[j][0] += cnt[v][type];
63             else if (dist[j][1] > distance + w)
64             {
65                 dist[j][1] = distance + w;
66                 cnt[j][1] = cnt[v][type];
67                 /* cout << j << " " << 1 << " " << cnt[j][1] << endl; */
68                 heap.push({j, 1, dist[j][1]});
69                 /* cout << "@@" << dist[j][1] << endl; */
70             }
71             else if (dist[j][1] == dist[v][type] + w)
72                 cnt[j][1] += cnt[v][type];
73         }
74     }
75 }
76 int main()
77 {
78     cin >> t;
79     while (t--)
80     {
81         cin >> n >> m;
82         vector<PII> g[N];
83         for (int i = 1; i <= m; i++)
84         {
85             int a, b, w;
86             cin >> a >> b >> w;
87             g[a].push_back({b, w});
88         }
89         cin >> s >> f;
90         dijkstra(s, f, g);
91         int res = cnt[f][0];
92         if (dist[f][1] == dist[f][0] + 1)
93             res += cnt[f][1];
94         cout << res << endl;
95     }
96     return 0;
97 }
复制代码

 注意一下在结构体中使用优先队列的方法

 《关于优先队列的写法与普通写法的区别》

这是我在使用普通数组的写法,注意观察内存已经爆掉了:

 

 

 这是我用vector的写法:

 

可以明显有好转

《floyd算法》

如果在考察两点之间的最短路时,能写floyd算法就写floyd算法,尽量不要去循环1~n,然后在循环中跑Dijkstra算法

因为这样表面上是O(n^3)/O(n^2logn)的算法,但是这样做常数特别大,很可能会超时

 

但是同样是O(n^3)的floyd算法常数特别小

《floyd原理》

参考博客:理论性:https://oi-wiki.org/graph/shortest-path/#floyd实践性,理解性:2

 

从动态规划的角度思考下面的内容会无比轻松:

 

 

 模拟:

首先从这张无向图入手,假设用floyd算法求其最小路:

  

 

 

 我们建立初始的,只能通过相邻点之间直接连接的距离 ,而这个距离也名副其实是最短的

 

 

 然后下面我们模拟的是floyd算法三重for循环中的第一重循环:

  从点1到点n,一个一个点进行“唤醒”,(假设这里的枚举的点为k)

  在这个第一重循环下面的两重循环 是枚举图上的任意两个点(假设分别为i和j),

  看一下是否能够通过上面枚举的点k进行更新 i和j点之间最短距离

 

 

 

 

 

重复上述过程...............

 

于是我们可以得到如下简单的代码:

  

 

 

 众所周知,dp是有可能进行消维的

 

 

 

 

 

 如何求最短路的路径?

  看这里<-----

有向边图咋办?

  在初始化图dis[][]时,将没有边的两点设为INF,有边的设为其边权,然后像求无向图一样即可

有决定最短路的有两个条件咋办?

  比如每个点有点权,在满足经过点最少的情况下,还有满足全部经过的点,点权之和最大

  设置d[][]点数量数组,d[i][j]表示从点i到点j的最短路中进过最少的点数为d[i][j]

  设置v[][]点权之和数组,在状态转移时,如果点数相等那么最大化点权之和

  

 

 

  注意这里点权之和v[][]有点考究:

 

  v[i][j]代表:点i到点j的最短路径上,点权之和但不包括点j的点权最大

 

  因为在状态转移的时候v[i][k]+v[k][j]

 

  v[i][j]如果设置成i,j之间全部点权之和,那么k点的点权就被加了两次

 

    有向边图初始化,为两点之间有边,点为自己,无边的情况:

 

  

 

 

 

 

 

 

 

 

 

  

 

本文作者:次林梦叶的小屋

本文链接:https://www.cnblogs.com/cilinmengye/p/16410057.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   次林梦叶  阅读(14)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起