bzoj 4398 福慧雙修 - 最短路
Description
菩薩為行,福慧雙修,智人得果,不忘其本。
——唐朠立《大慈恩寺三藏法師傳》
有才而知進退,福慧雙修,這才難得。
——烏雅氏
如何福慧雙修?被太後教導的甄嬛徘徊在禦花園當中。突然,她發現禦花園中的花朵全都是紅色和藍色的。她冥冥之中得到了響應:這就是指導她如何福慧雙修的! 現在禦花園可以看作是有N塊區域,M條小路,兩塊區域之間可通過小路連接起來。現在甄嬛站在1號區域,而她需要在禦花園中繞一繞,且至少經過1個非1號區 域的區域。但是恰好1號區域離碎玉軒最近,因此她最後還是要回到1號區域。由於太後教導她要福慧雙修,因此,甄嬛不能走過任何一條她曾經走過的路。但是, 禦花園中來往的奴才們太多了,而且奴才們前行的方向也不一樣,因此甄嬛在走某條小路的時候,方向不同所花的時間不一定一樣。天色快暗了,甄嬛需要盡快知道 至少需要花多少時間才能學會如何福慧雙修。如果甄嬛無法達到目的,輸出“-1”。
Input
第一行僅2個正整數n,m,意義如題。
接下來m行每行4個正整數s,t,v,w,其中s,t為小路所連接的兩個區域的編號,v為甄嬛從s到t所需的時間,w為甄嬛從t到s所需的時間。數據保證無重邊。
Output
僅一行,為甄嬛回到1號區域所需的最短時間,若方案不存在,則輸出-1
Sample Input
1 2 2 3
2 3 1 4
3 1 5 2
Sample Output
8HINT
[樣例解釋]
對於第一個數據:路徑為1->2->3->1,所需時間為8,而1->3->2->1所花時間為9。因此答案為8.
[數據範圍與約定]
對於40%的數據:n<=1,000; m<=5,000
對於100%的數據:1<=n<=40,000; 1<=m<=100,000; 1<=v,w<=1,000
題目大意
給定一個有向圖,對於連接同兩個點的邊算作同一條,問不經過重復邊的最小正權環。保證沒有重邊(這個是指有向的),沒有自環。
這道題經典就在於需要利用許多隱藏的性質。
如果想要暴力,那麽建圖多半會這麽建:
(ps:突然發現用ppt來畫圖特別贊)
比如顯而易見的第一條性質
性質1 從1號點開始spfa,回到點1’,發生重復走的邊一定是直接與1相連的邊
因為在中途一條邊走兩次,等於你回到原地,那麽肯定不優,所以spfa不會這麽幹。
於是就得到了一個優美的暴力做法,枚舉1剛走出的那條邊是哪條,然後刪掉它的"反向邊",然後最短路跑回來。於是菊花圖橫空出世,這個暴力做法完美TLE。
剛剛幹掉了不合法的方案,現在來思考一下如何幹掉不優的方案。
考慮最優的方案一定具有的性質。
-
如果最優的方案是直接跑最短路到達一個直接和1相連的點,但不是剛走出去的點。將它回到1的最後一條邊記為$e_{1}$
性質2 沒有更優的路徑通過$e_{1}$作為最後一條邊。
考慮用反證法,假設存在,那麽它和最短路的性質矛盾。所以不存在。
-
考慮用$p_{i}$表示從1號點出發,到達點$i$的最短路徑上的第二個點(剛離開1到達的點)。約定$p_{1} = -1$。
性質3 最優的路徑除了上面那種情況,路徑上僅存在1對直接相鄰的點對$(u, v)$,且$u \neq 1, v \neq 1$,滿足$p_{u} \neq p_{v}$
假如存在不為1對。如果存在0對,那麽就是上面那種情況。如果存在2對及其以上,比如下面這樣
圖中染有紫色、綠色和紅色的邊表示到某些點最短路徑。那麽顯然走$1\rightarrow a\rightarrow 1‘$會比之前的方案更優。可以一直像這麽做,直到滿足條件為止。
既然性質3這麽優美,那麽考慮如何好好利用它。
考慮邊$(b, a, w)$的時候(滿足$p_{a} \neq p_{b},a \neq 1, b\neq 1$),就在新圖中中建一條邊$(1, a, f_{b} + w)$(其中$f_{b}$表示原圖中1到b的最短路長度)。
如果邊$(b, a, w)$,滿足$p_{a} = p_{b},a \neq 1, b\neq 1$,那麽就保留這麽一條邊。
考慮邊$(1, v, w)$,如果滿足$p_{v} = v$,那麽就不管它。這樣可以使得新圖中的合法路徑滿足性質3。
然後再考慮如何好好利用性質2和性質1。
對於邊$(u, 1, w)$,如果滿足$p_{u}\neq u$,那麽直接在新圖中加邊$(1, 1‘, f_{u} + w)$
對於邊$(u, 1, w)$,如果滿足$p_{u} = u$,那麽在新圖中加邊$(u, 1‘, w)$。
還剩一種情況
對於邊$(1, v, w)$,滿足$p_{v} \neq v$如何處理。上面似乎沒有和它有關的性質。
這個保不保留你隨意,我試了試,保不保留都AC了。
原題的答案就是在新圖中1到1’的最短路。
最優性比較顯然,相信還有疑問,就是不會會出現走重復的邊的情況。
根據性質1,我們只需要考慮是否存在一種情況滿足$1\rightarrow u\rightarrow 1‘$的情況。
對於點$u$如果$p_{u} = u$,那麽新圖中的$u$只會和$1‘$連邊,否則只有邊連向$u$或者沒有有關$u$和1的連邊。所以不存在這種情況。
最後%%% ZJC一發,這人太強了。。一年前做這道題,我只會dfs,他想出了正解。
Code
1 /** 2 * bzoj 3 * Problem#4398 4 * Accepted 5 * Time: 416ms 6 * Memory: 6620k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 typedef class Edge { 13 public: 14 int end; 15 int next; 16 int w; 17 18 Edge(int end = 0, int next = 0, int w = 0):end(end), next(next), w(w) { } 19 }Edge; 20 21 typedef class MapManager { 22 public: 23 int ce; 24 int *h; 25 Edge* es; 26 27 MapManager() { } 28 MapManager(int n, int m):ce(0) { 29 h = new int[(n + 1)]; 30 es = new Edge[(m + 5)]; 31 memset(h, 0, sizeof(int) * (n + 1)); 32 } 33 34 void addEdge(int u, int v, int w) { 35 es[++ce] = Edge(v, h[u], w); 36 h[u] = ce; 37 } 38 39 Edge& operator [] (int pos) { 40 return es[pos]; 41 } 42 }MapManager; 43 44 int n, m; 45 MapManager g; 46 MapManager ng; 47 int* f; 48 int* ps; 49 boolean *vis; 50 51 inline void init() { 52 scanf("%d%d", &n, &m); 53 f = new int[(n + 2)]; 54 vis = new boolean[(n + 2)]; 55 g = MapManager(n, m << 1); 56 ng = MapManager(n + 1, m << 1); 57 ps = new int[(n + 1)]; 58 for (int i = 1, u, v, x, y; i <= m; i++) { 59 scanf("%d%d%d%d", &u, &v, &x, &y); 60 g.addEdge(u, v, x); 61 g.addEdge(v, u, y); 62 } 63 } 64 65 inline void spfa(MapManager& g, int s, boolean record) { 66 queue<int> que; 67 memset(f, 0x3f, sizeof(int) * (n + 2)); 68 memset(vis, false, sizeof(boolean) * (n + 2)); 69 que.push(s); 70 f[s] = 0, ps[s] = s; 71 while (!que.empty()) { 72 int e = que.front(); 73 que.pop(); 74 vis[e] = false; 75 for (int i = g.h[e]; i; i = g[i].next) { 76 int eu = g[i].end, w = g[i].w; 77 if (f[e] + w < f[eu]) { 78 if (record) 79 ps[eu] = (e == 1) ? (eu) : (ps[e]); 80 f[eu] = f[e] + w; 81 if (!vis[eu]) { 82 vis[eu] = true; 83 que.push(eu); 84 } 85 } 86 } 87 } 88 } 89 90 inline void rebuild() { 91 spfa(g, 1, true); 92 for (int i = g.h[1]; i; i = g[i].next) { 93 int e = g[i].end; 94 if (ps[e] != e) 95 ng.addEdge(1, e, g[i].w); 96 } 97 for (int i = 2; i <= n; i++) { 98 for (int j = g.h[i]; j; j = g[j].next) { 99 int e = g[j].end; 100 if (e == 1) { 101 if (ps[i] == i) 102 ng.addEdge(i, n + 1, g[j].w); 103 else 104 ng.addEdge(1, n + 1, f[i] + g[j].w); 105 } else { 106 if (ps[i] == ps[e]) 107 ng.addEdge(i, e, g[j].w); 108 else 109 ng.addEdge(1, e, f[i] + g[j].w); 110 } 111 } 112 } 113 } 114 115 inline void solve() { 116 spfa(ng, 1, false); 117 printf("%d\n", f[n + 1]); 118 } 119 120 int main() { 121 init(); 122 rebuild(); 123 solve(); 124 return 0; 125 }
bzoj 4398 福慧雙修 - 最短路