• 题解:《算法竞赛进阶指南》道路与航线


    农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。

    他想把牛奶送到 TT 个城镇,编号为 1∼T1∼T。

    这些城镇之间通过 RR 条道路 (编号为 11 到 RR) 和 PP 条航线 (编号为 11 到 PP) 连接。

    每条道路 ii 或者航线 ii 连接城镇 AiAi 到 BiBi,花费为 CiCi。

    对于道路,0≤Ci≤10,0000≤Ci≤10,000;然而航线的花费很神奇,花费 CiCi 可能是负数(−10,000≤Ci≤10,000−10,000≤Ci≤10,000)。

    道路是双向的,可以从 AiAi 到 BiBi,也可以从 BiBi 到 AiAi,花费都是 CiCi。

    然而航线与之不同,只可以从 AiAi 到 BiBi。

    事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策:保证如果有一条航线可以从 AiAi 到 BiBi,那么保证不可能通过一些道路和航线从 BiBi 回到 AiAi。

    由于约翰的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。

    他想找到从发送中心城镇 SS 把奶牛送到每个城镇的最便宜的方案。

    输入格式

    第一行包含四个整数 T,R,P,ST,R,P,S。

    接下来 RR 行,每行包含三个整数(表示一个道路)Ai,Bi,CiAi,Bi,Ci。

    接下来 PP 行,每行包含三个整数(表示一条航线)Ai,Bi,CiAi,Bi,Ci。

    输出格式

    第 1..T1..T 行:第 ii 行输出从 SS 到达城镇 ii 的最小花费,如果不存在,则输出 NO PATH

    数据范围

    1≤T≤250001≤T≤25000,
    1≤R,P≤500001≤R,P≤50000,
    1≤Ai,Bi,S≤T1≤Ai,Bi,S≤T

    输入样例:

    1. 6 3 3 4
    2. 1 2 5
    3. 3 4 5
    4. 5 6 10
    5. 3 5 -100
    6. 4 6 -100
    7. 1 3 -10

    输出样例:

    1. NO PATH
    2. NO PATH
    3. 5
    4. 0
    5. -95
    6. -100

    大概思路:
    将所有双向道路变为一个连通块,对于每个单向道路的边连接的都是连通块;
    因此该图转化为一个拓扑图,在每个连通块中求dijkstra,每个单向道路中用
    拓扑排序,最后dist[i]即为答案; 

    解题思路:
    1:先输入所有双向道路,再用dfs求出所有双向道路组成的连通块
    id[N]记录该点N在哪个连通块中, block[N]记录在连通块N中的点有多少个
    2:输入所有航线同时统计每个连通块的入度
    3:遍历所有连通块,将入度为0的点所在的连通块放入队列q中
    4:对于放入队列q中的连通块将每个连通块中的所有的点用dijkstra求最短路
    5:每次从dijkstra的堆中取出距离最小的点,然后枚举他的下一个点,
    若id[ver] == id[j]则表示他们在同一个连通块中继续枚举;
    若id[ver] != id[j]说明他们在不同的连通块中,id[j]连通块的入度要减一;
    若--din[id[j]] == 0则将他放入拓扑排序的q中 

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define x first
    7. #define y second
    8. using namespace std;
    9. typedef pair<int, int> PII;
    10. const int N = 25010, M = 150010, INF = 0x3f3f3f3f;
    11. int bcnt;
    12. int id[N];
    13. int din[N];
    14. bool st[N];
    15. int dist[N];
    16. queue<int> q;
    17. int n, mr, ml, S;
    18. vector<int> block[N];
    19. int h[N], e[M], ne[M], w[M], idx;
    20. void add(int a, int b, int c)
    21. {
    22. e[idx] = b;
    23. w[idx] = c;
    24. ne[idx] = h[a];
    25. h[a] = idx;
    26. idx ++ ;
    27. }
    28. void dfs(int u, int bid)
    29. {
    30. id[u] = bid;//记录点u在bid这个连通块中
    31. block[bid].push_back(u);//记录bid这个连通块中存在点u
    32. for (int i = h[u]; i != - 1; i = ne[i])
    33. {
    34. int j = e[i];
    35. if (!id[j])//继续枚举该联通块中的其他点
    36. dfs(j, bid);
    37. }
    38. }
    39. void dijkstra(int bid)
    40. {
    41. priority_queue, greater> heap;
    42. for (auto ver : block[bid]) heap.push({dist[ver], ver});//将bid这个连通块中的所有点入队
    43. while (heap.size())
    44. {
    45. auto t = heap.top();
    46. heap.pop();
    47. int distance = t.x, ver = t.y;
    48. if (st[ver]) continue;
    49. st[ver] = true;
    50. for (int i = h[ver]; i != -1; i = ne[i])
    51. {
    52. int j = e[i];
    53. if (id[j] != id[ver] && -- din[id[j]] == 0) q.push(id[j]);//若id[ver] != id[j]说明他们在不同的连通块中,id[j]连通块的入度要减一;
    54. //若--din[id[j]] == 0则将他放入拓扑排序的q中
    55. if (dist[j] > dist[ver] + w[i])
    56. {
    57. dist[j] = dist[ver] + w[i];
    58. if (id[j] == id[ver]) heap.push({dist[j], j});//说明他们在同一个连通块中,继续枚举下一个数
    59. }
    60. }
    61. }
    62. }
    63. void topsort()
    64. {
    65. memset(dist, 0x3f, sizeof dist);//初始化距离
    66. dist[S] = 0;
    67. for (int i = 1; i <= bcnt; i ++ )
    68. if (!din[i]) q.push(i);//枚举每个连通块,加入入度为0的点
    69. while (q.size())
    70. {
    71. auto t = q.front();
    72. q.pop();
    73. dijkstra(t);//对于t这个连通块,用dijkstra求出连通块中的最短路
    74. }
    75. }
    76. int main()
    77. {
    78. cin >> n >> mr >> ml >> S;
    79. memset(h, -1, sizeof h);
    80. while (mr -- )
    81. {
    82. int a, b, c;
    83. scanf("%d %d %d", &a, &b, &c);
    84. add(a, b, c), add(b, a, c);
    85. }
    86. for (int i = 1; i <= n; i ++ )//枚举所有的连通块
    87. if (!id[i])
    88. dfs(i, ++ bcnt);
    89. while (ml -- )
    90. {
    91. int a, b, c;
    92. scanf("%d %d %d", &a, &b, &c);
    93. add(a, b, c);
    94. din[id[b]] ++ ;//将每个b所在的连通块的的入度++
    95. }
    96. topsort();
    97. for (int i = 1; i <= n; i ++ )
    98. if (dist[i] > INF / 2) puts("NO PATH");//因为存在负边,所以dist[j] > dist[i] + w[i]若w[i]
    99. else printf("%d\n", dist[i]); //是负数,则无穷大dist[j]也能更新无穷大dist[i]因此要判断
    100. //它是不是大于一个极大的数表示无解而不是 是否等于INF;
    101. return 0;
    102. }

     

  • 相关阅读:
    【学习笔记】minIO分布式文件服务系统
    Leetcode 1662. 检查两个字符串数组是否相等
    REST-assured简介
    【Shell篇四】Shell运算符与test命令
    等保测评实施与改善
    拼多多API详情接口调用示例
    [NOIP2011 提高组] Mayan 游戏
    深度学习与总结JVM专辑(三):垃圾回收器—G1(图文+代码)
    查询物流有多简单,具体步骤如下
    MacOS设置JAVA_HOME环境变量
  • 原文地址:https://blog.csdn.net/qq_61935738/article/details/126360524