题目描述:

德克萨斯纯朴的民众们这个夏天正在遭受巨大的热浪!!!

他们的德克萨斯长角牛吃起来不错,可是它们并不是很擅长生产富含奶油的乳制品。

农夫John此时身先士卒地承担起向德克萨斯运送大量的营养冰凉的牛奶的重任,以减轻德克萨斯人忍受酷暑的痛苦。

John已经研究过可以把牛奶从威斯康星运送到德克萨斯州的路线。

这些路线包括起始点和终点一共有 T 个城镇,为了方便标号为 1 到 T。

除了起点和终点外的每个城镇都由 双向道路 连向至少两个其它的城镇。

每条道路有一个通过费用(包括油费,过路费等等)。

给定一个地图,包含 C 条直接连接 2 个城镇的道路。

每条道路由道路的起点 Rs,终点 Re 和花费 Ci 组成。

求从起始的城镇 Ts 到终点的城镇 Te 最小的总费用。

输入格式

第一行: 4 个由空格隔开的整数: T,C,Ts,Te;

第 22 到第 C+1 行: 第 i+1 行描述第 i 条道路,包含 3 个由空格隔开的整数: Rs,Re,Ci。

输出格式

一个单独的整数表示从 Ts 到 Te 的最小总费用。

数据保证至少存在一条道路。

数据范围

1≤T≤2500,
1≤C≤6200,
1≤Ts,Te,Rs,Re≤T,
1≤Ci≤1000

输入样例:

7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1

输出样例:

7

分析:

本题考查最简单的无负权的单源最短路,鉴于很久没刷图论题了,借本题回忆下单源最短路的几种算法,算法基础课中关于单源最短路的详细题解见AcWing 849 Dijkstra求最短路 I,AcWing 850 Dijkstra求最短路 II,AcWing 853 有边数限制的最短路,AcWing 851 spfa求最短路,分别介绍了朴素版的dijkstra算法、堆优化版的dijkstra算法、BellmanFord算法以及spfa算法。下面用这四种算法来解决本题。

方法一:朴素版dijkstra(O(n^2))

简述下朴素dijkstra算法的步骤:每次在还没被标记的点集里找出里起点距离最近的点,标记为已访问,同时用该点的距离去更新周围的点,由于每次都有一个顶点被标记为已访问,所以最多循环n次所有的点都被标记成已访问了。已经被标记的顶点离起点的距离是最小的,不会被更新了。

细节上要注意的有,一般朴素版dijkstra算法采用邻接矩阵实现,时间复杂度是O(n^2)。算法执行前要将三个数组都初始化一下,将存储图的邻接矩阵的初始值初始化为INF(这个很容易忽略,不初始化的话不存在边的顶点之间的边权就默认是0了),将距离数组d初始为为INF,只将起点的距离初始化为0,将访问标记数组st初始化为false。另外,无向图需要存两遍边。

分析下为什么每次找到还没被标记的点集中d最小的点一定不会被更新了,因为已经被标记的点集已经对周围的点都执行了松弛操作,设已经被标记的点集是S,未被标记的点集中有两点a和b,离点集S最近的点是a,假设此时a的d不是最小的,也就是说后面d还能被其他未被标记的点松弛,假设是b点,则d[a] > d[b] + dist[a][b],又d[a] < d[b],dist[a][b]是正权边,所以相互矛盾,故a的d一定是最小的。

总体来说本题的顶点数不是很大,用朴素版dijkstra算法解决效率还不错。

#include <iostream>
#include <cstring>
using namespace std;
const int N = 2505;
int g[N][N],d[N],s,e,n,m;
bool st[N];
int dijkstra(){memset(d,0x3f,sizeof d);memset(st,false,0);d[s] = 0;for(int i = 0;i < n;i++){//遍历n次int t = -1;for(int j = 1;j <= n;j++){if(!st[j] && (t == -1 || d[j] < d[t]))  t = j;}st[t] = true;if(t == e)  break;for(int j = 1;j <= n;j++){if(!st[j] && d[t] + g[t][j] < d[j]) d[j] = d[t] + g[t][j];//松弛操作}}return d[e];
}
int main(){scanf("%d%d%d%d",&n,&m,&s,&e);int a,b,c;memset(g,0x3f,sizeof g);for(int i = 0;i < m;i++){scanf("%d%d%d",&a,&b,&c);g[a][b] = g[b][a] = c;}cout<<dijkstra()<<endl;return 0;
}

方法二:堆优化版dijkstra(O(mlogn))

堆优化版dijkstra算法相对于朴素版的dijkstra算法最大的改动就是找还未被标记的点集中离起点最近的点时时不再是遍历所有的点,而是用一个小根堆动态的去维护顶点的距离,这使得时间复杂度降低到了O(mlogn),然而堆优化版的代码上相对于朴素版的代码变化却远不止这一处。

首先是存储上的变化,如果依旧采用邻接矩阵存储,堆优化版的dijkstra每次松弛操作依旧是O(n)的,这样时间复杂度依旧是平方级别的,所以需要采用邻接表存储。邻接表存储的过程是:存储a到b权重为c的边,首先将第idx 条边的终点b用e[idx]存储下,然后权重c用w[idx]存储下,接着采用头插法将这条边插到顶点a邻接表的首位,先用ne[idx]保存原先h[a]的第一条边的编号,在将h[a]的第一条边更新为idx,同时idx++。最后两步的顺序不能改变,采用头插法是为了防止断链。

然后细节的变化,朴素版算法固定循环n次,而堆优化版的外循环要改成优先级队列非空时继续循环,优先级队列默认是大根堆,这里存入距离的相反数就可以当小根堆用了。堆优化版dijkstra最大的难点在于对标志数组st的理解,出队的顶点执行了松弛操作就会被标记为true,下次再出队就break,不会再去松弛了,这样做的目的只是为了避免冗余。每个顶点第一次作为堆中距离最小的元素出队后,便不会再被松弛,不会再次入队,但是,在它出队之前,是可能多次入队的,比如a第一次被b更新进入了堆里,然后又被c松弛再次入队,就会出现堆中有多个a的问题,但是a第一次出队取得的必然是a的最小距离,所以不影响,用st数组剪枝下可以基本上杜绝执行无用的松弛操作。

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int,int> PII;
const int N = 2505,M = 12500;
int d[N],s,en,n,m;
int idx,h[N],e[M],ne[M],w[M];
priority_queue<PII> pq;
bool st[N];
void add(int a,int b,int c){e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
int dijkstra(){memset(d,0x3f,sizeof d);memset(st,false,0);d[s] = 0;pq.push({-d[s],s});while(pq.size()){int t = pq.top().second;pq.pop();if(st[t])   continue;st[t] = true;if(t == en)  break;for(int i = h[t];~i;i = ne[i]){int j = e[i];if(!st[j] && d[t] + w[i] < d[j]){d[j] = d[t] + w[i];//松弛操作 pq.push({-d[j],j});} }}return d[en];
}
int main(){scanf("%d%d%d%d",&n,&m,&s,&en);int a,b,c;memset(h,-1,sizeof h);for(int i = 0;i < m;i++){scanf("%d%d%d",&a,&b,&c);add(a,b,c),add(b,a,c);}cout<<dijkstra()<<endl;return 0;
}

方法三:BellmanFord(O(nm))

如果说dijkstra算法是用已经是最短距离的点去更新周围的点,那么BellmanFord算法就是尝试遍历每条边去更新所有的点。如果限制最短路径长度不能超过k,那么第一次边的更新会更新到距离起点路径长度是1的点,第k次更新会更新到路径长度是k的点,为了防止串联需要备份d数组,何谓串联,就是a更新了b,b又更新了c,则再这次循环中a实际更新了更远的点,每次循环更新的路径长度边不是1了。当然,本题没有k条边限制,发生串联还有助于松弛更快,所以无须备份。

由于BellmanFord算法需要遍历边集,所以用一个结构体数组来存储所有的边,结构体的成员包括边的起点、终点以及边权,无向图需要存储两次。然后算法的流程就是执行n-1次利用边权去更新顶点的操作,由于第k次循环距离起点路径长度是k的点都已经被更新成了最小距离,所以最多n-1次就更新完了整个图。万一第n次还有点的距离被更新了呢?就说明存在负权回路了,没有最短距离,如果没有负权回路,只是有负权边则不影响求最短路径,比如a到b的边权是-1,没有负权回路完全不用担心每次执行边的松弛a都会让b的距离减小,因为a的距离更新一定次数后是不变的,所以加上边权的值不论被更新多少次都是不变的。

BellmanFord算法较为简单,没有标志数组,几行代码就实现了,时间复杂度是O(nm)。

#include <iostream>
#include <cstring>
using namespace std;
typedef pair<int,int> PII;
const int N = 2505,M = 12500;
int d[N],s,en,n,m,idx;
struct Node{int x,y,w;
}edge[M];
int bellman_ford(){memset(d,0x3f,sizeof d);d[s] = 0;for(int i = 1;i < n;i++){for(int j = 0;j < idx;j++){if(d[edge[j].y] > d[edge[j].x] + edge[j].w)   d[edge[j].y] = d[edge[j].x] + edge[j].w;}}return d[en];
}
int main(){scanf("%d%d%d%d",&n,&m,&s,&en);int a,b,c;for(int i = 0;i < m;i++){scanf("%d%d%d",&a,&b,&c);edge[idx++] = {a,b,c};edge[idx++] = {b,a,c};}cout<<bellman_ford()<<endl;return 0;
}

方法四:spfa(O(m))

BellmanFord的弊端在于每次都要遍历所有的边,即使这次遍历并不能松弛顶点的距离。实际上,只有上一轮中被松弛的顶点才会去松弛相邻的顶点,因此可以将每轮松弛的顶点用一个队列存下来,只去松弛队列中顶点相邻的顶点,一般情况下spfa的复杂度是O(m),但是可能常数比较大,效率略低于堆优化版dijkstra。

梳理下spfa算法的流程:首先将初始顶点加入队列,取队头元素并将队头元素出队,尝试用队头元素去松弛周围的顶点,被松弛的顶点将继续加入到队列中。另外,已经在队列里的元素不用再次入队了,更新下它的距离即可,下次出队会按照最新的距离去执行松弛操作的。这里的st数组表示顶点是否在队列中,入队时置true,出队时置false,可以反复入队出队。而dijkstra是否中的st数组表示的是该顶点是否已经出过优先级队列,已经出队的d就是最小了,不必再次入队了,这就是两个算法st数组意义的不同之处。

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int,int> PII;
const int N = 2505,M = 12500;
int d[N],s,en,n,m;
int idx,h[N],e[M],ne[M],w[M];
queue<int> q;
bool st[N];
void add(int a,int b,int c){e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
int spfa(){memset(d,0x3f,sizeof d);memset(st,false,0);d[s] = 0;q.push(s);st[s] = true;while(q.size()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i = ne[i]){int j = e[i];if(d[j] > d[u] + w[i]){d[j] = d[u] + w[i];if(!st[j]){q.push(j);st[j] = true; }}}}return d[en];
}
int main(){scanf("%d%d%d%d",&n,&m,&s,&en);int a,b,c;memset(h,-1,sizeof h);for(int i = 0;i < m;i++){scanf("%d%d%d",&a,&b,&c);add(a,b,c),add(b,a,c);}cout<<spfa()<<endl;return 0;
}

AcWing 1129 热浪相关推荐

  1. 算法提高课-图论-单源最短路的建图方式-AcWing 1129. 热浪:dijkstra裸题

    题目分析 来源:acwing 分析: ac代码 朴素的dijkstra() ,时间复杂度O(n2)O(n^2)O(n2) #include<bits/stdc++.h> using nam ...

  2. acwing 1129. 热浪(spfa)

    题目 热浪题目链接 德克萨斯纯朴的民众们这个夏天正在遭受巨大的热浪!!! 他们的德克萨斯长角牛吃起来不错,可是它们并不是很擅长生产富含奶油的乳制品. 农夫John此时身先士卒地承担起向德克萨斯运送大量 ...

  3. 【ACWing】1129. 热浪

    题目地址: https://www.acwing.com/problem/content/1131/ 德克萨斯纯朴的民众们这个夏天正在遭受巨大的热浪!!!他们的德克萨斯长角牛吃起来不错,可是它们并不是 ...

  4. 算法——AcWing算法提高课中代码和题解

    文章目录 第一章 动态规划 (完成情况:64/68) 数字三角形模型 最长上升子序列模型 背包模型 状态机模型 状态压缩DP 区间DP 树形DP 数位DP 单调队列优化DP 斜率优化DP 第二章 搜索 ...

  5. AcWing算法提高课

    1. 动态规划(43/68) 1.1 数字三角形模型(4/4) 1.1.1 AcWing 1015. 摘花生 结论: f[i][j]=max⁡(f[i−1][j],f[i][j−1])+w[i][j] ...

  6. 单/全源最短路的综合应用(未完待续...)

    目录 一.单源最短路 1.1 AcWing 1129. 热浪 1.2 AcWing 1128. 信使 1.3 AcWing 1127. 香甜的黄油 1.4 AcWing 1126. 最小花费 1.5 ...

  7. AcWing算法提高课 Level-3 第三章 图论

    单源最短路的建图方式 1129. 热浪 思路 :单源最短路算法中除了bellmanford一般不用以外,普D为O(n2)O(n^2)O(n2),优D为O(m∗logn)O(m*logn)O(m∗log ...

  8. 单源最短路的建图方式(Dijkstra)

    由于是复习,所以不会解释太多. 主要为Dijkstra的堆优化板子和朴素版(看数据范围) 再次看看时间复杂度[ n 为点数,m 为边数 ]:朴素版:O(),堆优化版:O( (n+m)logm ). 目 ...

  9. 第三章 图论【未完成】

    目录 单源最短路的建图方式 1129. 热浪[最普通的最短路板子] 1128. 信使[基本的最短路 + 稍微的扩展] 1127. 香甜的黄油[稍微变种的最短路] 1126. 最小花费[Dijkstra ...

最新文章

  1. 晶体管逻辑芯片设计微缩图形化
  2. Linux getopt()函数 getopt_long()函数---转
  3. 使用OpenSSH远程管理Linux服务器
  4. dracut 查看linux分区,Centos进入dracut模式,报 /dev/centos/swap does not exist,如何恢复
  5. 暴露的全局方法_期末考试快到了,这4种错误的考试复习方法,越复习越差劲!...
  6. bash 别名_Linux的10个方便的Bash别名
  7. ideal如何快速导入import_Vue性能优化:如何实现延迟加载和代码拆分?
  8. 初译 Support Vector Machines:A Simple Tutorial(一)
  9. 电流电压曲线 vc源码_电瓶修复—充电曲线你知道多少?
  10. microsoft edge怎么截长图_苹果12怎么截图截长屏 iphone 12截长图快捷键方法介绍
  11. Jrebel激活服务,Jrebel激活,Jrebel激活码,Jrebel破解
  12. python 打包exe_python打包exe能运行但是没有结果解决方案
  13. Linux系统中修复SambaCry漏洞(CVE-2017-7494)
  14. 计算机高级培训教师感言,教师信息技术培训感言信息技术培训心得多篇.docx
  15. unity 打包APK 应用未安装
  16. css33d图片轮播_手把手教你用纯css3实现轮播图效果
  17. E.Neko and Flashback
  18. mysql统计去重记录数量_MySQL分组后,如何统计记录条数
  19. 进程三态与五态是什么?
  20. 快衰落,慢衰落,大尺度衰落,小尺度衰落的关系

热门文章

  1. Yolo 一文看懂目标检测
  2. 【深度学习】:详解目标检测YOLO V1(You Only Look Once)算法
  3. 飞书接入ChatGPT,打造属于自己的智能问答助手
  4. 阿里巴巴实习一年之后的感悟
  5. 用Python 编写Logistics算法
  6. 日本警方称地震造成3676人死亡7843人下落不明
  7. bootcmd执行过程
  8. T a(v);和T a = v;的区别
  9. 2020全国计算机考试ps版本,2020年3月计算机等级Photoshop练习题及参考答案
  10. 电脑上怎么同时录制系统和麦克风声音