网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
heap.push({dist[j], j});
}
}
}
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main () {
cin >> n >> m;
memset(h, -1, sizeof h);
while (m --) {
int a,b,c;
scanf(“%d%d%d”, &a, &b, &c);
add(a, b, c);
}
printf(“%d\n”, dijkstra());
return 0;
}
### 二、含负权边
### Bellman-ford 【O(nm)】
>
> 适用:限制边,有向,负权,稠密图
> **应用:**
> 从A出发是否存在到达各个节点的路径(如果不是无穷则表示存在);
> 从A出发到达各个节点最短路径(时间最少、路径最少…)
> 图中是否存在负环路(权重之和为负数)
>
>
>
[题目链接](https://bbs.csdn.net/topics/618668825)
**注意题目中限制了经过的边数 所以存在负权回路(转圈圈) 也不影响**
![在这里插入图片描述](https://img-blog.csdnimg.cn/18d34aedec604b5f8980ba28f8e0a8b0.png)
#### 先看代码
#include
#include
#include
using namespace std;
const int N = 510,M = 10010;
int n,m,k;
int dist[N],backup[N];
struct Edge {
int a,b,w;
}edges[M];
void ballman_ford () {
memset(dist,0x3f,sizeof dist);//初始化
dist[1] = 0;
for(int i = 0; i < k; i ++) { //迭代k次
memcpy(backup,dist,sizeof dist);
for(int j = 0; j < m; j ++) {
int a = edges[j].a,b = edges[j].b,w = edges[j].w;
dist[b] = min(dist[b], backup[a] + w);
}
}
if(dist[n] > 0x3f3f3f3f / 2) { //如果是一个很大的数 必然不可能
printf(“impossible”);
}
else {
printf(“%d\n”,dist[n]);
}
}
int main () {
scanf(“%d%d%d”,&n, &m, &k);
for(int i = 0; i < m; i++) {
int a,b,w;
scanf(“%d%d%d”,&a, &b, &w);
edges[i] = {a,b,w};
}
ballman_ford();
return 0;
}
* 遍历每一个点 并且个点遍历所有的边
* 原理是对图进行最多`n-1`次松弛操作,得到所有可能的最短路径。
`松弛操作` 其实就是更新当前最短距离,其中`dist[]`表示从起点到该点的距离。 `w` :权值
`dist[b]=min(dist[b],dist[a]+w);`
![在这里插入图片描述](https://img-blog.csdnimg.cn/48f0e30c0c0d41eca4703ea351cfe5f5.png)
* 此外还需要一个数组`backup` 来进行 备份
`backup[j]`表示每次进入第2重循环的dist数组的备份。
如果不加这个备份的话有可能会发生节点最短距离的串连
`串联`:就是多条边的值加到了一起。导致第`k`层循环之后找到一个边数大于`k`的路径。
* 如何理解迭代k次?
其中迭代`k`次表示:不超过`k`条边的最短路径
正确的理解方式:迭代一次表示俩个结点只能通过一条边到达
如果迭代k次 就是 可以走k条边的最短路径
### spfa求最短路【O(m) 最坏O(nm)】
[题目链接](https://bbs.csdn.net/topics/618668825)
![在这里插入图片描述](https://img-blog.csdnimg.cn/7e0f1ddbf72d423c9255f3efcf71abcd.png)
>
> 思路和迪杰斯特拉堆优化差不多
>
>
>
![在这里插入图片描述](https://img-blog.csdnimg.cn/8f84b2a0d523483ebed2cb262e604169.png)
>
> 这个也可以做dijkstraⅡ也能ac,而且还更快,手上的dijkstra瞬间不香了,但是如果是IO赛制最好还是用dijkstra,小心被出题人卡【y总的经验】
>
>
>
#include
#include
#include
#include
using namespace std;
const int N = 100010;
int idx,h[N],ne[N],w[N],e[N];
int dist[N];
bool st[N];
int n,m;
void add(int a,int b,int c) {
e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx ++;
}
void spfa() {
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue q;
q.push(1);
st[1] = true;//存的是当前的点是否在队列当中 防止存负重复的点
while(q.size()) {
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i] ) {
int j = e[i];
if(dist[j] > dist[t] + w[i]) {
dist[j] = dist[t] + w[i];
if(!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
if(dist[n] == 0x3f3f3f3f) {
puts(“impossible”);
} else {
printf(“%d\n”, dist[n]);
}
}
int main () {
cin >> n >> m;
memset(h, -1, sizeof h);
while (m --) {
int a,b,c;
scanf(“%d%d%d”, &a, &b, &c);
add(a, b, c);
}
spfa();
return 0;
}
### spfa求负环
>
> 基于上面的思路 ,多加了一个数组`cnt[]`,表示这个点存在的边数,根据`抽屉原理`可得该路线存在环,又因为求的是最短的边,所以只能是负环。
> [题目链接](https://bbs.csdn.net/topics/618668825)
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/39a32e8cf5b04ce588ce91b37120d18d.png)
>
>
>
#include
#include
#include
#include
using namespace std;
const int N = 100010;
int idx,h[N],ne[N],w[N],e[N];
int dist[N],cnt[N];
bool st[N];
int n,m;
void add(int a,int b,int c) {
e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx ++;
}
bool spfa() {
queue q;
for(int i = 1; i <= n; i ++) {
st[i] = true;
q.push(i);
}
while(q.size()) {
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i] ) {
int j = e[i];
if(dist[j] > dist[t] + w[i]) {
dist[j] = dist[t] + w[i];
cnt[j] = cnt[t] + 1;
if(cnt[j] >= n) {
return true;
}
if(!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
return false;
}
int main () {
cin >> n >> m;
memset(h, -1, sizeof h);
while (m --) {
int a,b,c;
scanf(“%d%d%d”, &a, &b, &c);
add(a, b, c);
}
if(spfa()) {
puts(“Yes”);
} else {
puts(“No”);
}
return 0;
}
## 多源汇最短路
### Floyd 算法 【O(n3) 】
>
> `Floyd` 属于多源最短路径算法,能够求出任意`2`个顶点之间的最短路径,支持负权边,基于动态规划.
>
>
>
闫氏DP分析法:
`d[k,i,j]` :从`i`点出发,只经过`k`的点到达`j`的距离的所有集合。
属性:`MIN`
选:`d[k - 1][i][j]`//不经过k点
不选: `f[k - 1][i][k] + [k - 1][k][j]`//经过k点
状态转移方程(优化成一维):`dist[i][j] = min(dist[i][j],dist[i][k] + dist[k][j])`
#### C++代码
#include
#include
#include
using namespace std;
const int N = 210,INF = 1e9;
int n, m, Q;
int d[N][N];
void floyd() {
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
oid floyd() {
[外链图片转存中…(img-H9kLIT5E-1715791886778)]
[外链图片转存中…(img-AnmoQcCQ-1715791886778)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!