• SPFA算法(判断负权回路,求最短路径)(851,852)


    851. spfa求最短路

    给定一个 n

    个点 m

    条边的有向图,图中可能存在重边和自环, 边权可能为负数

    请你求出 1

    号点到 n 号点的最短距离,如果无法从 1 号点走到 n

    号点,则输出 impossible

    数据保证不存在负权回路。

    输入格式

    第一行包含整数 n

    和 m

    接下来 m

    行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z

    输出格式

    输出一个整数,表示 1

    号点到 n

    号点的最短距离。

    如果路径不存在,则输出 impossible

    数据范围

    1≤n,m≤105

    ,
    图中涉及边长绝对值均不超过 10000

    输入样例:

    1. 3 3
    2. 1 2 5
    3. 2 3 -3
    4. 1 3 4

    输出样例:

    2
    

     解法一:SPFA算法

     * Bellman_Ford 算法是每一轮松弛迭代都会对所有边进行比较,看是否能够进行松弛操作,这就
     * 造成了很多没必要的比较,想想,只有当该边被松弛了,那么后面与该边相连接的边才能够被
     * 松弛, 因此我们可以只记录被松弛的边,没被松弛的边就不用管它。
     * 这就是著名的SPFA算法,我们用一个队列来存储被松弛的边。

    1. /**
    2. * Bellman_Ford 算法是每一轮松弛迭代都会对所有边进行比较,看是否能够进行松弛操作,这就
    3. * 造成了很多没必要的比较,想想,只有当该边被松弛了,那么后面与该边相连接的边才能够被
    4. * 松弛, 因此我们可以只记录被松弛的边,没被松弛的边就不用管它。
    5. * 这就是著名的SPFA算法,我们用一个队列来存储被松弛的边。
    6. */
    7. #include <iostream>
    8. #include <algorithm>
    9. #include <queue>
    10. #include <vector>
    11. using namespace std;
    12. struct ENode
    13. {
    14. int v,w;
    15. };
    16. const int maxn = 1e5+10 ,INF=1e9;
    17. vector<ENode> Adj[maxn]; //邻接表int d[maxn];
    18. int d[maxn]; //距离数组
    19. bool hs[maxn]; //记录顶点是否被选择
    20. int Nv,Ne; //顶点数,边数
    21. void Read() //读入数据
    22. {
    23. cin >> Nv >> Ne;
    24. for(int i=0;i<Ne;++i)
    25. {
    26. int u,v,w;
    27. cin >> u >> v >> w;
    28. Adj[u].push_back({v,w});
    29. }
    30. }
    31. void SPFA(int st)
    32. {
    33. queue<int> q;
    34. q.push(st);
    35. fill(d,d+maxn,INF);
    36. d[st]=0;
    37. hs[st]=1;
    38. while(q.size())
    39. {
    40. int u=q.front();
    41. q.pop();
    42. hs[u]=0;
    43. for(int i=0;i<Adj[u].size();++i)
    44. {
    45. int v=Adj[u][i].v,w=Adj[u][i].w;
    46. if(d[u]+w < d[v])
    47. {
    48. d[v]=d[u]+w;
    49. if(hs[v]==0)
    50. q.push(v);
    51. }
    52. }
    53. }
    54. }
    55. int main()
    56. {
    57. Read();
    58. SPFA(1);
    59. if(d[Nv] > INF/2)
    60. puts("impossible");
    61. else
    62. cout << d[Nv] << endl;
    63. return 0;
    64. }

    解法二:Bellman_Ford算法

    /**
     * Bellman_Ford 算法:经过Nv-1 条边松弛,从源点到达各点的最短路;
     * 为什么是 Nv-1 条边,因为Nv个没有回路的顶点的图,最多只有Nv-1条边;
     * 如果进行Nv-1条边松弛以后,顶点还能被松弛,则一定存在负权回路;
     * 因此Bellman_Ford 算法可以求解图是否有负权回路存在。
     *
     * 对于Bellnan_Ford 算法最坏时间复杂度是 O(NM),但是往往还未达到Nv-1轮松弛前就已经计算出
     * 最短路径了,所以我们可以用一个 flag变量来标记此轮松弛操作是否该改变了dis数组的值,如果
     * 没有改变,就提前退出,可以减少相当多的时间消耗;
     *
     * 我说怎么能AC,原来是保证不会存在负权回路,当有负权回路的时候,该算法一定是最坏时间复
     * 杂度;
    */

    1. /**
    2. * Bellman_Ford 算法:经过Nv-1 条边松弛,从源点到达各点的最短路;
    3. * 为什么是 Nv-1 条边,因为Nv个没有回路的顶点的图,最多只有Nv-1条边;
    4. * 如果进行Nv-1条边松弛以后,顶点还能被松弛,则一定存在负权回路;
    5. * 因此Bellman_Ford 算法可以求解图是否有负权回路存在。
    6. *
    7. * 对于Bellnan_Ford 算法最坏时间复杂度是 O(NM),但是往往还未达到Nv-1轮松弛前就已经计算出
    8. * 最短路径了,所以我们可以用一个 flag变量来标记此轮松弛操作是否该改变了dis数组的值,如果
    9. * 没有改变,就提前退出,可以减少相当多的时间消耗;
    10. *
    11. * 我说怎么能AC,原来是保证不会存在负权回路,当有负权回路的时候,该算法一定是最坏时间复
    12. * 杂度;
    13. */
    14. #include <iostream>
    15. #include <algorithm>
    16. #include <queue>
    17. #include <vector>
    18. using namespace std;
    19. struct ENode
    20. {
    21. int v,w;
    22. };
    23. typedef pair<int,int> PII;
    24. const int maxn = 1e5+10 ,INF=1e9;
    25. vector<ENode> Adj[maxn]; //邻接表int d[maxn];
    26. int d[maxn]; //距离数组
    27. bool hs[maxn]; //记录顶点是否被选择
    28. int Nv,Ne; //顶点数,边数
    29. void Read() //读入数据
    30. {
    31. cin >> Nv >> Ne;
    32. for(int i=0;i<Ne;++i)
    33. {
    34. int u,v,w;
    35. cin >> u >> v >> w;
    36. Adj[u].push_back({v,w});
    37. }
    38. }
    39. void Bellman_Ford(int st) //各点到st顶点的最短距离
    40. {
    41. fill(d,d+maxn,INF);
    42. d[st]=0;
    43. for(int k=1;k<Nv;++k)
    44. {
    45. int flag=0;
    46. for(int u=1;u<=Nv;++u)
    47. for(int i=0;i<Adj[u].size();++i)
    48. {
    49. int v=Adj[u][i].v,w=Adj[u][i].w;
    50. if(d[u]+w < d[v])
    51. {
    52. d[v]=d[u]+w;
    53. flag=1;
    54. }
    55. }
    56. if(flag == 0)
    57. break;
    58. }
    59. }
    60. int main()
    61. {
    62. Read();
    63. Bellman_Ford(1);
    64. if(d[Nv] > INF/2)
    65. puts("impossible");
    66. else
    67. cout << d[Nv] << endl;
    68. return 0;
    69. }

    852. spfa判断负环

    给定一个 n

    个点 m

    条边的有向图,图中可能存在重边和自环, 边权可能为负数

    请你判断图中是否存在负权回路。

    输入格式

    第一行包含整数 n

    和 m

    接下来 m

    行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z

    输出格式

    如果图中存在负权回路,则输出 Yes,否则输出 No

    数据范围

    1≤n≤2000

    ,
    1≤m≤10000,
    图中涉及边长绝对值均不超过 10000

    输入样例:

    1. 3 3
    2. 1 2 -1
    3. 2 3 4
    4. 3 1 -4

    输出样例:

    Yes
    

     解法一:SPFA算法

    /**
     * Bellman_Ford 算法是每一轮松弛迭代都会对所有边进行比较,看是否能够进行松弛操作,这就
     * 造成了很多没必要的比较,想想,只有当该边被松弛了,那么后面与该边相连接的边才能够被
     * 松弛, 因此我们可以只记录被松弛的边,没被松弛的边就不用管它。
     * 这就是著名的SPFA算法,我们用一个队列来存储被松弛的边。
     *
     * 如果每一个顶点被Nv条边进行了松弛操作(一个无自环的顶点,最多只有Nv-1条边与其相连),
     * 那么一定出现了负环;用一个 num 数组来记录每个顶点被松弛的边的数目
    */

    1. /**
    2. * Bellman_Ford 算法是每一轮松弛迭代都会对所有边进行比较,看是否能够进行松弛操作,这就
    3. * 造成了很多没必要的比较,想想,只有当该边被松弛了,那么后面与该边相连接的边才能够被
    4. * 松弛, 因此我们可以只记录被松弛的边,没被松弛的边就不用管它。
    5. * 这就是著名的SPFA算法,我们用一个队列来存储被松弛的边。
    6. *
    7. * 如果每一个顶点被Nv条边进行了松弛操作(一个无自环的顶点,最多只有Nv-1条边与其相连),
    8. * 那么一定出现了负环;用一个 num 数组来记录每个顶点被松弛的边的数目
    9. */
    10. #include <iostream>
    11. #include <algorithm>
    12. #include <queue>
    13. #include <vector>
    14. using namespace std;
    15. struct ENode
    16. {
    17. int v,w;
    18. };
    19. const int maxn = 1e5+10 ,INF=1e9;
    20. vector<ENode> Adj[maxn]; //邻接表int d[maxn];
    21. int d[maxn]; //距离数组
    22. int num[maxn]; //记录每个顶点有多少条边能够使其松弛
    23. bool hs[maxn]; //记录顶点是否被选择
    24. int Nv,Ne; //顶点数,边数
    25. void Read() //读入数据
    26. {
    27. cin >> Nv >> Ne;
    28. for(int i=0;i<Ne;++i)
    29. {
    30. int u,v,w;
    31. cin >> u >> v >> w;
    32. Adj[u].push_back({v,w});
    33. }
    34. }
    35. bool SPFA(int st) //出现负环,返回true
    36. {
    37. queue<int> q;
    38. fill(d,d+maxn,INF);
    39. d[st]=0;
    40. for(int i=1;i<=Nv;++i)
    41. {
    42. q.push(i); //要将所有顶点都入队,只入队某一个顶点,可能从该顶点出发并不能
    43. hs[i]=1; //到达负环
    44. }
    45. while(q.size())
    46. {
    47. int u=q.front();
    48. q.pop();
    49. hs[u]=0;
    50. for(int i=0;i<Adj[u].size();++i)
    51. {
    52. int v=Adj[u][i].v,w=Adj[u][i].w;
    53. if(d[u]+w < d[v])
    54. {
    55. d[v]=d[u]+w;
    56. num[v] = num[u]+1;
    57. if(num[v]>=Nv) //如果有Nv条边能够松弛v这个顶点(一个无自环的顶点,
    58. return true; //最多只有Nv-1条边与其相连—),那么一定出现了负环;
    59. if(hs[v]==0) //如果该顶点未在队列中,将其入队
    60. {
    61. q.push(v);
    62. hs[v]=1;
    63. }
    64. }
    65. }
    66. }
    67. return false;
    68. }
    69. int main()
    70. {
    71. Read();
    72. if(SPFA(1))
    73. puts("Yes");
    74. else
    75. puts("No");
    76. return 0;
    77. }

    解法二:Bellman_Ford 算法:

    1. /**
    2. * 松弛定理:d[u]+w < d[v] ;
    3. *
    4. * Bellman_Ford 算法:经过Nv-1 条边松弛,求得从源点到达各点的最短路;
    5. * 为什么是 Nv-1 条边,因为Nv个没有回路的顶点的图,最多只有Nv-1条边;
    6. * 一定是没有回路的,因为假设是正回路,那么去掉这条这条回路,一定
    7. * 能得到更短的距离,如果是负回路,那么一定得不到最短路;
    8. *
    9. * 如果进行Nv-1条边松弛以后,顶点还能被松弛,则一定存在负权回路;
    10. * 因此Bellman_Ford 算法可以求解图是否有负权回路存在。
    11. *
    12. * 对于要判断是否有负权回路这种情况,即要进行 Nv-1 轮所有边的松弛操作,可以不使用pre数组,
    13. * 因为进行Nv-1轮或者进行Nv轮迭代没什么区别,如果有负权值,那么进行Nv轮迭代以后边还是能够
    14. * 松弛;
    15. */
    16. #include <iostream>
    17. #include <algorithm>
    18. #include <queue>
    19. #include <vector>
    20. #include <cstring>
    21. using namespace std;
    22. struct ENode
    23. {
    24. int v,w;
    25. };
    26. typedef pair<int,int> PII;
    27. const int maxn = 2010 ,INF=1e9;
    28. vector<ENode> Adj[maxn]; //邻接表int d[maxn];
    29. int d[maxn]; //距离数组
    30. bool hs[maxn]; //记录顶点是否被选择
    31. int Nv,Ne; //顶点数,边数
    32. void Read() //读入数据
    33. {
    34. cin >> Nv >> Ne;
    35. for(int i=0;i<Ne;++i)
    36. {
    37. int u,v,w;
    38. cin >> u >> v >> w;
    39. Adj[u].push_back({v,w});
    40. }
    41. }
    42. int Bellman_Ford(int st) //判断图中是否存在回路
    43. {
    44. fill(d,d+maxn,INF);
    45. d[st]=0;
    46. for(int k=1;k<Nv;++k)
    47. {
    48. for(int u=1;u<=Nv;++u)
    49. for(int i=0;i<Adj[u].size();++i)
    50. {
    51. int v=Adj[u][i].v,w=Adj[u][i].w;
    52. if(d[u]+w < d[v])
    53. d[v]=d[u]+w;
    54. }
    55. }
    56. for(int u=1;u<=Nv;++u)
    57. for(int i=0;i<Adj[u].size();++i)
    58. {
    59. int v=Adj[u][i].v,w=Adj[u][i].w;
    60. if(d[u]+w < d[v]) //还能被松弛,存在负回路
    61. return 1;
    62. }
    63. return 0; //不存在负回路
    64. }
    65. int main()
    66. {
    67. Read();
    68. if(Bellman_Ford(1))
    69. puts("Yes");
    70. else
    71. puts("No");
    72. return 0;
    73. }

  • 相关阅读:
    049:mapboxGL本地上传WKT文件,在地图上显示图形
    构建现代应用:Java中的热门架构概览
    【机器学习基础】正则化
    大数据平台进度,它来了
    简单又有效的基本折线图制作方法
    LNMP架构
    Redis分区/分片详解
    免费的内网穿透(钉钉)
    Linux 命令(199)—— arp 命令
    HybridCLR 使用流程记录
  • 原文地址:https://blog.csdn.net/qq_51825761/article/details/126760341