1. 程式人生 > >bzoj 4398 福慧雙修 - 最短路

bzoj 4398 福慧雙修 - 最短路

bzoj otto for esp accep 不存在 目的 solid 前行

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

3 3
1 2 2 3
2 3 1 4
3 1 5 2

Sample Output

8

HINT

[樣例解釋]

對於第一個數據:路徑為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相連的點,但不是剛走出去的點。將它回到1的最後一條邊記為$e_{1}$

    性質2 沒有更優的路徑通過$e_{1}$作為最後一條邊。

    考慮用反證法,假設存在,那麽它和最短路的性質矛盾。所以不存在。

  2. 考慮用$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 福慧雙修 - 最短路