B. Complete The Graph

B. Complete The Graph

ZS the Coder has drawn an undirected graph of n vertices numbered from 0 to n1 and m edges between them. Each edge of the graph is weighted, each weight is a positive integer.

The next day, ZS the Coder realized that some of the weights were erased! So he wants to reassign positive integer weight to each of the edges which weights were erased, so that the length of the shortest path between vertices s and t in the resulting graph is exactly L. Can you help him?

Input

The first line contains five integers n, m, L, s, t (2n1000,1m10000,1L109,0s,tn1,st) — the number of vertices, number of edges, the desired length of shortest path, starting vertex and ending vertex respectively.

Then, m lines describing the edges of the graph follow. i-th of them contains three integers, ui, vi, wi (0ui,vin1,uivi,0wi109). ui and vi denote the endpoints of the edge and wi denotes its weight. If wi is equal to 0 then the weight of the corresponding edge was erased.

It is guaranteed that there is at most one edge between any pair of vertices.

Output

Print "NO" (without quotes) in the only line if it's not possible to assign the weights in a required way.

Otherwise, print "YES" in the first line. Next *m* lines should contain the edges of the resulting graph, with weights assigned to edges which weights were erased. i-th of them should contain three integers ui, vi and wi, denoting an edge between vertices ui and vi of weight wi. The edges of the new graph must coincide with the ones in the graph from the input. The weights that were not erased must remain unchanged whereas the new weights can be any positive integer not exceeding 1018.

The order of the edges in the output doesn't matter. The length of the shortest path between s and t must be equal to L.

If there are multiple solutions, print any of them.

Examples

input

5 5 13 0 4
0 1 5
2 1 2
3 2 3
1 4 0
4 3 4

output

YES
0 1 5
2 1 2
3 2 3
1 4 8
4 3 4

input

2 1 123456789 0 1
0 1 0

output

YES
0 1 123456789

input

2 1 999999999 1 0
0 1 1000000000

output

NO

Note

Here's how the graph in the first sample case looks like :

In the first sample case, there is only one missing edge weight. Placing the weight of 8 gives a shortest path from 0 to 4 of length 13.

In the second sample case, there is only a single edge. Clearly, the only way is to replace the missing weight with 123456789.

In the last sample case, there is no weights to assign but the length of the shortest path doesn't match the required value, so the answer is "NO".

 

解题思路

  leetcode的周赛的题目Modify Graph Edge Weights,直接搬了cf上的这道题,这里就写一下这道题的题解吧。

  定义边权为0的边为变量边。首先,我们先忽略所有的变量边,对这个图跑一边从st的最短路,得到最短距离dt。如果dt<L那么无解,因为无论我们给变量边赋什么值(正整数),从st的最短路都不会超过dt

  再考虑把所有的变量边赋值为1,然后再跑一遍从st的最短路。此时如果dt>L那么无解,因为每条变量边赋值最小为1,如果增大变量边的值那么dt只会变大,故从st的最短路永远不会是L

  此时我们就排除了两种无解的情况,实际上只要排除了这两种情况那么一定有解。当有解时,结合第一种情况,此时有结论st不经过变量边的最短路一定大于等于L而结合第二种情况,此时一定存在从st距离小于等于L的路径,并且根据结论知道这些路径一定是包含变量边的,因此从st的最短路一定包含变量边。因此我们可以修改最短路中变量边的值,使得从st的路径长度恰好为L,同时由于我们只关心从st的最短路中的变量边,因此可以将不在最短路中的变量边均赋值为正无穷(109+1即可)。不过需要注意的是虽然使得这条最短路的长度变成了L,但可能还会存在其他从st距离小于L的路径。由于从st长度小于L的路径一定包含变量边,这时我们只需要重复上面的步骤去修改最短路中的变量边,直到最短路恰好为L(即不存在从st距离小于L的路径)。这就证明了一定有解。

  算法流程就是上面的做法,我们需要找到一条当前从st的最短路径,然后再去修改最短路径上的变量边。同时由于每条边的权值为正整数,因此从st最多会经过n1条边,所以我们最多修改n1条经过st路径上的变量边就可以使得最短路恰好为L。这里跑最短路用的是Dijkstra算法,在跑最短路时用一个path数组记录每个点是从哪个点转移来的,这样就可以得到从st的最短路径所经过的所有点。然后还需要存每条边与编号的映射,具体来说有无向边(u,v),由于节点编号为0n1,因此可以把边映射为单个整数un+vvn+u。然后根据path数组与映射关系来枚举从st的最短路径所经过的边,选择任意一条变量边(不失一般性这里选择第一条枚举到的变量边),将其赋值为Ldt+1,即可将当前的最短路径长度变成L。同时把不在这条最短路上的变量边赋值为正无穷。最后再跑一次最短路,看看是否满足dt=L,满足则输出答案,否则继续重复上面的步骤。

  AC代码如下,时间复杂度为O(n(n+m)logm)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 typedef pair<LL, int> PII;
 6 
 7 const int N = 1010, M = 2e4 + 10;
 8 
 9 int n, m, l, s, t;
10 int head[N], e[M], wts[M], ne[M], idx;
11 LL dist[N];
12 bool vis[N];
13 int path[N];    // 记录最短每个点从哪个点转移过来
14 int mp[N * N];  // 存边与编号的映射
15 unordered_set<int> st;  // 存变量边的编号
16 
17 void add(int v, int w, int wt) {
18     e[idx] = w, wts[idx] = wt, ne[idx] = head[v], head[v] = idx++;
19 }
20 
21 void dijkstra() {
22     memset(dist, 0x3f, sizeof(dist));
23     dist[s] = 0;
24     priority_queue<PII, vector<PII>, greater<PII>> pq;
25     pq.push({0, s});
26     memset(vis, 0, sizeof(vis));
27     memset(path, -1, sizeof(path));
28     while (!pq.empty()) {
29         int t = pq.top().second;
30         pq.pop();
31         if (vis[t]) continue;
32         vis[t] = true;
33         for (int i = head[t]; i != -1; i = ne[i]) {
34             if (dist[e[i]] > dist[t] + wts[i]) {
35                 dist[e[i]] = dist[t] + wts[i];
36                 path[e[i]] = t;
37                 pq.push({dist[e[i]], e[i]});
38             }
39         }
40     }
41 }
42 
43 int main() {
44     scanf("%d %d %d %d %d", &n, &m, &l, &s, &t);
45     memset(head, -1, sizeof(head));
46     for (int i = 0; i < m; i++) {
47         int v, w, wt;
48         scanf("%d %d %d", &v, &w, &wt);
49         mp[v * n + w] = idx, mp[w * n + v] = idx + 1;   // 把无向边(v,w)映射到v*n+w和w*n+v
50         if (!wt) st.insert(idx), st.insert(idx + 1);    // 记录变量边
51         add(v, w, wt), add(w, v, wt);
52     }
53     for (auto &i : st) {    // 第一次跑最短路需要忽略变量边,赋值为正无穷即可
54         wts[i] = 1e9 + 1;
55     }
56     dijkstra();
57     if (dist[t] < l) {  // 从s到t的最短路<L,无解
58         printf("NO");
59         return 0;
60     }
61     for (auto &i : st) {    // 再把所有的变量边赋值为1跑最短路
62         wts[i] = 1;
63     }
64     dijkstra();
65     if (dist[t] > l) {  // 从s到t的最短路>L,无解
66         printf("NO\n");
67         return 0;
68     }
69     else if (dist[t] < l) { // 有解
70         while (dist[t] < l) {   // 只要从s到t的最短路小于L则重复过程,最多执行n-1次
71             for (auto &i : st) {    // 先把所有变量边赋值为正无穷,之后对从s到t最短路上的变量边进行赋值
72                 wts[i] = 1e9 + 1;
73             }
74             int p = t;
75             bool flag = true;   // 用来记录是否枚举到第一条变量边
76             while (path[p] != -1) {
77                 int i = mp[p * n + path[p]], j = mp[path[p] * n + p];   // 得到这条边的编号
78                 if (st.count(i)) {  // 这条边是变量边
79                     if (flag) wts[i] = wts[j] = l - dist[t] + 1, flag = false;  // 枚举到第一条变量边,赋值为L-dt+1
80                     else wts[i] = wts[j] = 1;   // 其他的变量边赋值为1
81                 }
82                 p = path[p];
83             }
84             dijkstra(); // 再跑一次最短路
85         }
86     }
87     printf("YES\n");
88     for (int i = 0; i < n; i++) {
89         for (int j = head[i]; j != -1; j = ne[j]) {
90             if (i < e[j]) printf("%d %d %d\n", i, e[j], wts[j]);
91         }
92     }
93     
94     return 0;
95 }
复制代码

 

参考资料

  Codeforces Round #372 Editorial:https://codeforces.com/blog/entry/47169

  第 346 场力扣周赛:https://leetcode.cn/circle/discuss/fwWHZg/view/3muHjG/

posted @   onlyblues  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2021-05-25 Hashing - Hard Version
Web Analytics
点击右上角即可分享
微信分享提示