【算法基础】 Acwing 图论 之 最短路 (Dijkstra Bellman_ford Spfa Floyd))_acwing 最短路

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事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() {

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

oid floyd() {

[外链图片转存中…(img-H9kLIT5E-1715791886778)]
[外链图片转存中…(img-AnmoQcCQ-1715791886778)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值