当前位置:   article > 正文

2023秋招算法题每日学习(4)_不爱施肥的小布

不爱施肥的小布

DAY 4

1.AcWing 850. Dijkstra求最短路 ii

考察点:堆优化Dijkstra,求最短路问题 适合于稀疏图,利用邻接表来存储

邻接表不需要对重边做特殊的处理

(1)基础知识

时间复杂度分析:

堆优化Dijkstra

堆优化版Dijkstra与朴素版的区别主要就是在用寻找最小距离t时采用的方式来实现。堆其实看做是一种完全二叉树的数组对象,是最高效的优先级队列,分为小根堆和大根堆。

堆的实现方式:

   手写堆优先队列
n个数,方便插入和修改一个数不支持修改任意一个数,实现方式是冗余 m个数,但是两种方式的时间复杂度相同

优先队列:(可以解决一些贪心问题,以及Dijkstra的优化问题)

      1.  priority_queue(优先队列),底层是用堆的数据结构来实现,队首元素为队列中优先级最高的一个,在插入数据时,采用冗余的实现方式,优先队列底层的数据结构堆(heap)会随时调整结构。

       2. 使用时需要添加#include<queue>,using namespace std;

        其定义写法为priority_queue<typename>name;

       3.  优先队列只能通过top()函数来访问队首元素,优先级最高(堆顶元素),没有front(),back()函数,具有push(),top(),empty().q.size();

       4. 队列内优先级设置:priority_queue<int,vector<int>,less<int>>q

        队首是优先级最高的,参数2 vector<int>是承载底层数据结构堆的容器,可以根据类型修改vector的数据类型,参数3是对第一个参数的比较类,less<int>表示数字大的优先级越大(大根堆),great<int>表示数字小的优先级越大(小根堆 

最终题解代码

        

  1. #include<iostream>
  2. #include<queue>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. typedef pair<int,int>PII;//第一个参数存储距离起点的距离,第二个参数存储是第几个数
  7. const int N = 1e6+10;
  8. int n, m;
  9. int h[N],e[N],ne[N],w[N],idx;//为稀疏图,所以采用邻接表来存储,w[N]为权重
  10. int dist[N];//用于记录每一个点距离第一个点的距离
  11. bool st[N];//用于记录该点的最短距离是否已经确定
  12. void add(int a,int b,int c)
  13. {
  14. e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
  15. }
  16. int Dijkstra()
  17. {
  18. memset(dist, 0x3f,sizeof dist); //初始化距离 0x3f代表无限大
  19. dist[1] = 0;//第一个点到自身的距离为0
  20. priority_queue<PII,vector<PII>,greater<PII>> heap;//采用小根堆
  21. heap.push({0,1});
  22. while(heap.size())
  23. {
  24. auto t = heap.top();
  25. heap.pop();
  26. int ver = t.second, distance = t.first;//以t为中转站,依次更新每个点所到相邻的点路径值
  27. if (st[ver]) continue;
  28. st[ver] = true;//遍历t
  29. for(int i = h[ver]; i != -1; i = ne[i])
  30. {
  31. int j = e[i];
  32. if (dist[j] > dist[ver] + w[i])
  33. {
  34. dist[j] = dist[ver] + w[i];
  35. heap.push({dist[j], j});
  36. }
  37. }
  38. }
  39. if(dist[n]==0x3f3f3f3f) return -1; //如果第n个点路径为无穷大即不存在最低路径
  40. return dist[n];
  41. }
  42. int main()
  43. {
  44. scanf("%d%d", &n, &m);
  45. memset(h, -1, sizeof h);//初始化邻接表
  46. while(m--)
  47. {
  48. int a, b, c;
  49. scanf("%d%d%d", &a, &b, &c);
  50. add(a,b,c);//不需要考虑重边
  51. }
  52. printf("%d\n", Dijkstra());
  53. return 0;
  54. }

2..AcWing 853. 有边数限制的最短路

考察点:bellman-ford(权重存在负数)

(1)bellman-ford的存储f方式非常多,可以使用邻接表,也可以使用结构体来存储图

(2)Bellman - ford 算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在 n-1 次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。
(通俗的来讲就是:假设 1 号点到 n 号点是可达的,每一个点同时向指向的方向出发,更新相邻的点的最短距离,通过循环 n-1 次操作,若图中不存在负环,则 1 号点一定会到达 n 号点,若图中存在负环,则在 n-1 次松弛后一定还会更新)

(3)bellman - ford算法的具体步骤
for n次
for 所有边 a,b,w (松弛操作)
dist[b] = min(dist[b],back[a] + w)

注意:back[] 数组是上一次迭代后 dist[] 数组的备份,由于是每个点同时向外出发,因此需要对 dist[] 数组进行备份,若不进行备份会因此发生串联效应,影响到下一个点

 

 

 注:能求出来最短路的情况下是没有负权回路的(spfa),bellman-ford是可能的

(5) 时间复杂度:O(nm) 在题目中如果有对边数进行限制的时候,只能使用bellman-ford算法,其他情况下一般都可以被性能更好的算法替换。

需要注意在t对后续点进行更新的时候,需要备份,防止发生距离串联,影响边数限制。

(6)strcpy和memcpy主要有以下3方面的区别。

复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,如果空间不够,就会引起内存溢出。memcpy则是根据其第3个参数决定复制的长度。
用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy,由于字符串是以“\0”结尾的,所以对于在数据中包含“\0”的数据只能用memcpy。
void *memcpy(void *dest,const void *src,size_t count);

函数说明:memcpy()用来拷贝src所指内容的内存前的count个字节到dest所指的内存地址上,memcpy会赋值完整的count个字节,不会因为遇到字符串结束'\0'而结束

最终解题代码:

注:为什么是dist[n]>0x3f3f3f3f/2, 而不是dist[n]>0x3f3f3f3f
        5号节点距离起点的距离是无穷大,利用5号节点更新n号节点距离起点的距离,将得到109−2109−2, 虽然小于109109, 但并不存在最短路,(在边数限制在k条的条件下)。

 

  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. const int N = 510,M = 10010;
  6. struct Edge
  7. {
  8. int a,b,c;
  9. }edges[M];//把每个边保存下来,将自定义的结构体放入到数组中方便维护
  10. int dist[N];
  11. int last[N];//备份数据防止串联
  12. int n,m,k;//k代表最多限制经过k条边
  13. void bellman_ford()
  14. {
  15. memset(dist, 0x3f, sizeof dist);//初始化
  16. dist[1] = 0;
  17. for (int i = 0; i < k; i++) //k次循环
  18. {
  19. memcpy(last, dist, sizeof dist);//备份未更新前的数据
  20. for (int j = 0; j < m; j++) //遍历所有边
  21. {
  22. auto e = edges[j];
  23. dist[e.b] = min(dist[e.b], last[e.a] + e.c); //使用back:避免给a更新后立马更新b, 这样b一次性最短路径就多了两条边出来
  24. }
  25. }
  26. }
  27. int main() {
  28. scanf("%d%d%d", &n, &m, &k);
  29. for (int i = 0; i < m; i++)
  30. {
  31. int a, b, c;
  32. scanf("%d%d%d", &a, &b, &c);
  33. edges[i] = {a, b, c};
  34. }
  35. bellman_ford();
  36. if (dist[n] > 0x3f3f3f3f / 2) puts("impossible");
  37. else printf("%d\n", dist[n]);
  38. return 0;
  39. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/121281?site
推荐阅读
相关标签
  

闽ICP备14008679号