[CODEFORCES 1307D Cow and Fields](https://codeforces.com/contest/1307/problem/D) = ## 題目大意 給連通圖,有 $n$ 個點 $m$ 條雙向邊 沒有重邊,沒有自環,點從 $1$ 到 $n$ 編號 這些點中有 $k$ 個特殊點,要**只挑兩點**加入一條雙向邊,使圖最短路徑從 $1$ 到 $n$ 盡量長 > 意思是若是加 $(a, b)$ 邊後算出的最短路比加 $(b, c)$ 邊後還要短 > 那麼要選 $(b, c)$ 邊 ==若特殊點與特殊點間原本有邊,還是能加邊== ## 輸入 $n, m, k$ $k$ 個特殊點 $a_1, a_2, \cdots , a_k$ $m$ 對點 (邊) ## 輸出 最短路徑長度 ## 解法 1 明顯的,在加入一條邊之後或多或少會讓最短路更短 - 如果更短了,肯定是最短路徑用了新加的這條邊 - 如果沒有改善,那就是最短路徑沒有改走新加的邊 也就是說,只需關心**當最短路徑用了新加的邊**的狀況 那麼設此邊為 $(a, b)$ 則要讓 $1 \rightsquigarrow n$ 盡量長, 而路徑是 $1 \rightsquigarrow a \to b \rightsquigarrow n$ 或 $1 \rightsquigarrow b \to a \rightsquigarrow n$ > 這裡 $x \rightsquigarrow y$ 是 $x$ 到 $y$ 的最短路徑 設 $d_x(y)$ 為 $x \rightsquigarrow y$ 的長度 > 則 $1 \rightsquigarrow a \to b \rightsquigarrow n$ 的長度就是 $d_1(a) + 1 + d_n(b)$ 若最短路徑是 $1 \rightsquigarrow a \to b \rightsquigarrow n$ 則長度會有 $d_1(a) + 1 + d_n(b) \le d_1(b) + 1 + d_n(a)$ 而任意特殊點對(邊) $(a, b)$ 都得滿足此不等式,也就是例如有點對 $(a, b), (b, c), (c, a)$ 會有三個不等式要滿足,假設為 $d_1(a) + 1 + d_n(b) < d_1(b) + 1 + d_n(a)$ $d_1(b) + 1 + d_n(c) < d_1(c) + 1 + d_n(b)$ $d_1(c) + 1 + d_n(a) < d_1(a) + 1 + d_n(c)$ > 在特殊點為三個以上,就不會出現等於關係了 會發現這些不等式能化簡成 $d_1(a) - d_n(a) < d_1(b) - d_n(b)$ $d_1(b) - d_n(b) < d_1(c) - d_n(c)$ $d_1(c) - d_n(c) < d_1(a) - d_n(a)$ 再化簡 $\underline{d_1(a) - d_n(a)} < d_1(b) - d_n(b) < d_1(c) - d_n(c) < \underline{d_1(a) - d_n(a)}$ 會發現得得到矛盾的結論 所以須從確定的結論往前推,不失一般性的,設有 $d_1(a) - d_n(a) < d_1(b) - d_n(b) < d_1(c) - d_n(c)$ 則回推的不等式為 $d_1(a) + 1 + d_n(b) < d_1(b) + 1 + d_n(a)$ $d_1(b) + 1 + d_n(c) < d_1(c) + 1 + d_n(b)$ $d_1(a) + 1 + d_n(c) < d_1(c) + 1 + d_n(a)$ 所以要比較這三者的大小 $d_1(a) + 1 + d_n(b), d_1(b) + 1 + d_n(c), d_1(a) + 1 + d_n(c)$ 推廣至一般狀況,若是 $d_1(a_1)-d_n(a_1) < d_1(a_2)-d_n(a_2) < \cdots < d_1(a_k)-d_n(a_k)$ 則需要比較所有 $d_1(a_x) + 1 + d_n(a_y)$ 其中 $1 \le x < y \le k$ ```cpp #include<bits/stdc++.h> using namespace std; int const maxn = 2e5 + 10; int n, m, k, x, y; vector<int> E[maxn], a; void bfs(int s, vector<int> &d) { queue<int> q; set<int> vis; q.push(s); vis.insert(s); d[s] = 0; while(q.size()) { int u = q.front(); q.pop(); for(int v: E[u]) { if(vis.count(v)) continue; vis.insert(v); d[v] = d[u] + 1; q.push(v); } } } int main() { scanf("%d%d%d", &n, &m, &k); a.resize(k); for(int i = 0; i < k; i++) scanf("%d", &a[i]); for(int i = 0; i < m; i++) { scanf("%d%d", &x, &y); E[x].push_back(y); E[y].push_back(x); } vector<int> d1(n+1), dn(n+1); bfs(1, d1); bfs(n, dn); sort(a.begin(), a.end(), [&](int x, int y) { return d1[x]-dn[x] < d1[y]-dn[y]; }); int ans = 0, d1_mx = 0; for(int i = 0; i+1 < k; i++) { d1_mx = max(d1_mx, d1[a[i]]); ans = max(ans, d1_mx + 1 + dn[a[i+1]]); } printf("%d\n", min(ans, d1[n])); return 0; } ``` ## 解法 2 同樣只關心有加入新邊的情況 設特殊點 $a, b$ 滿足 $d_1(a) < d_1(b)$ 有雙向邊 $(a, b)$,則有三種情況需考慮﹔ - $d_1(n) < d_1(a) < d_1(b)$ 最短路徑不經過 $a, b$ - $d_1(a) < d_1(n) < d_1(b)$ 最短路徑不經過 $b$ - $d_1(a) < d_1(b) < d_1(n)$ $d_1(a) + 1 < d_1(b) + 1$,且 $d_n(b)$ 與 $d_n(a)$ 只相差 $1$ 若是 $x \in \{a, b\}. d_1(x) + d_n(x) > d_1(n)$,則最短路徑不經過 $x$。 由上推得 $d_1(a) + 1 + d_n(b) \le d_1(b) + 1 + d_n(a)$ 所以特殊點依照 BFS 的深度排列,比較所有 $d_1(a_x) + 1 + d_n(a_y)$ 其中 $1 \le x < y \le k$ 得解 ```cpp #include<bits/stdc++.h> using namespace std; int const maxn = 2e5 + 10; int n, m, k, a, x, y; vector<int> E[maxn], ord; // ord := order vector<bool> sp; // sp := special void bfs(int s, vector<int> &d) { queue<int> q; q.push(s); d.assign(n+1, -1); d[s] = 0; while(q.size()) { int u = q.front(); q.pop(); if(s == 1 && sp[u]) ord.push_back(u); for(int v: E[u]) { if(d[v] != -1) continue; d[v] = d[u] + 1; q.push(v); } } } int main() { scanf("%d%d%d", &n, &m, &k); sp.assign(n+1, false); for(int i = 0; i < k; i++) scanf("%d", &a), sp[a] = true; for(int i = 0; i < m; i++) { scanf("%d%d", &x, &y); E[x].push_back(y); E[y].push_back(x); } vector<int> d1, dn; bfs(1, d1); bfs(n, dn); int ans = 0, d1_mx = 0; for(int i = 0; i+1 < ord.size(); i++) { d1_mx = max(d1_mx, d1[ord[i]]); ans = max(ans, d1_mx + 1 + dn[ord[i+1]]); } printf("%d\n", min(ans, d1[n])); return 0; } ```