福慧雙修(both)
福慧雙修(both)
題目描述:
菩薩為行,福慧雙修,智人得果,不忘其本。
——唐·慧立《大慈恩寺三藏法師傳》
有才而知進退,福慧雙修,這才難得。
——烏雅氏
如何福慧雙修?被太後教導的甄嬛徘徊在禦花園當中。突然,她發現禦花園中的花朵全都是紅色和藍色的。她冥冥之中得到了響應:這就是指導她如何福慧雙修的!現在禦花園可以看作是有N塊區域,M條小路,兩塊區域之間可通過小路連接起來。現在甄嬛站在1號區域,而她需要在禦花園中繞一繞,且至少經過1個非1號區域的區域。但是恰好1號區域離碎玉軒最近,因此她最後還是要回到1號區域。由於太後教導她要福慧雙修,因此,甄嬛不能走過任何一條她曾經走過的路。但是,禦花園中來往的奴才們太多了,而且奴才們前行的方向也不一樣,因此甄嬛在走某條小路的時候,方向不同所花的時間不一定一樣。天色快暗了,甄嬛需要盡快知道至少需要花多少時間才能學會如何福慧雙修。如果甄嬛無法達到目的,輸出“-1”。
輸入格式:
第一行僅2個正整數n,m,意義如題。
接下來m行每行4個正整數s,t,v,w,其中s,t為小路所連接的兩個區域的編號,v為甄嬛從s到t所需的時間,w為甄嬛從t到s所需的時間。數據保證無重邊。
輸出格式:
僅一行,為甄嬛回到1號區域所需的最短時間,若方案不存在,則輸出-1
樣例輸入:
樣例一
3 3
1 2 2 3
2 3 1 4
3 1 5 2
樣例二
3 2
1 2 1 1
2 3 1 2
樣例輸出:
樣例一
8
樣例二
-1
數據範圍:
對於40%的數據:n<=1,000; m<=5,000
對於100%的數據:1<=n<=40,000; 1<=m<=100,000; 1<=v,w<=1,000
時間限制:
1s
空間限制:
256M
不得不說,這題很難想,隨便亂打了一個DFS也沒有拿到分...(菜啊)正解很鬼畜,想都想不到.
首先,我們肯定要跑一遍spfa,求出每一個點到1的最短距離dis[i].
接下來,我們要用到一個數組,這個數組非常的重要,設pre[i]表示這條路徑上與1相連的點的標號。這樣我們就可以通過這個pre數組推測出一條邊是否已被使用.
顯然,在spfa後只有直接與1相連的點的pre值為其本身,其他的點的pre值都等於其前驅節點的pre值.
由於最後題目讓我們回到點1,所以,我們需要建立一個新圖,將匯點變為n+1,同時,要在原圖的基礎上改一下,建一個新圖.
那麽,我們要通過pre數組來搞事情了:
1.若pre[x]!=x
說明從1到x的最短路沒有經過這條邊,這條邊可以在新圖中建立,邊權為w.
2.若pre[x]==x
說明從1到x的最短路通過這條邊,這條邊不能建立.
因為,我們將會用這條比較優秀的邊去建立新圖的邊,這樣,如果建了這條邊,這條邊可能會被用2次或更多.
b.如果有一條從x到1的邊,邊權為w:
1.若pre[x]!=x
說明從1到x的最短路沒有經過這條邊,這條邊的可以被建立,因此我們可以等效地在新圖中建立一條從起點1到終點n+1的邊,邊權為w+dis[x]
2.若pre[x]==x
說明從1到x的最短路通過了這條邊,我們把這條邊等效轉移一下,從x->1變為x->n+1,邊權為w.
c.如果有一條起終點均不為1的邊,邊權為w.
1.若pre[u]!=pre[v],說明原點到達這兩端點,經過的最短路徑是不同的.
什麽意思呢?就是說邊對於u和v並沒有用過,所以我們可以在新圖中建立一條從1到v,邊權為dis[u]+w的邊(當然原邊都要刪除).
2.若pre[u]==pre[v],在新圖中保留原邊就行了,不要動它(它並沒有做什麽壞事啊qwq)。
註意,新圖建好後,需要把dis數組重新初始化,因為dis數組已經在建圖時已經發揮了它的作用,現在它就可以滾粗了...
最後,只要在新圖中再跑一次spfa,輸出dis[n+1]就好啦.
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<queue> 6 #define nnxt New_nxt 7 #define nson New_son 8 #define nw New_w 9 #define nlnk New_lnk 10 using namespace std; 11 const int maxe=200005,maxn=40005; 12 int n,m,tot,ntot,ans; 13 int nxt[maxe],son[maxe],w[maxe],lnk[maxn]; 14 int nnxt[maxe],nson[maxe],nw[maxe],nlnk[maxn]; 15 int dis[maxn],pre[maxn]; 16 bool vis[maxn]; 17 int read(){ 18 int x=0; char ch=getchar(); 19 while (ch<‘0‘||ch>‘9‘) ch=getchar(); 20 while (ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); 21 return x; 22 } 23 void add(int x,int y,int z){nxt[++tot]=lnk[x],son[tot]=y,w[tot]=z,lnk[x]=tot;} 24 void addn(int x,int y,int z){nnxt[++ntot]=nlnk[x],nson[ntot]=y,nw[ntot]=z,nlnk[x]=ntot;} 25 void spfa(){ 26 memset(vis,0,sizeof vis),vis[1]=1; 27 memset(dis,63,sizeof dis),dis[1]=0; 28 memset(pre,0,sizeof pre),pre[1]=1; 29 int x; queue <int> Q; while (!Q.empty()) Q.pop(); Q.push(1); 30 while (!Q.empty()){ 31 vis[x=Q.front()]=0,Q.pop(); 32 for (int j=lnk[x]; j; j=nxt[j]) 33 if (dis[son[j]]>dis[x]+w[j]){ 34 dis[son[j]]=dis[x]+w[j]; pre[son[j]]=pre[x]; 35 if (x==1) pre[son[j]]=son[j]; 36 if (!vis[son[j]]) vis[son[j]]=1,Q.push(son[j]); 37 } 38 } 39 } 40 void spfa_new(){ 41 memset(vis,0,sizeof vis),vis[1]=1; 42 memset(dis,63,sizeof dis),dis[1]=0; 43 int x; queue <int> Q; while (!Q.empty()) Q.pop(); Q.push(1); 44 while (!Q.empty()){ 45 vis[x=Q.front()]=0,Q.pop(); 46 for (int j=nlnk[x]; j; j=nnxt[j]) 47 if (dis[nson[j]]>dis[x]+nw[j]){ 48 dis[nson[j]]=dis[x]+nw[j]; 49 if (!vis[nson[j]]) vis[nson[j]]=1,Q.push(nson[j]); 50 } 51 } 52 } 53 int main(){ 54 n=read(),m=read(),tot=ntot=0,ans=1e9; 55 for (int i=1; i<=m; i++){ 56 int x=read(),y=read(),z1=read(),z2=read(); 57 add(x,y,z1),add(y,x,z2); 58 } 59 spfa(); 60 for (int i=1; i<=n; i++) 61 for (int j=lnk[i]; j; j=nxt[j]) 62 if (i==1){ 63 if (pre[son[j]]!=son[j]) addn(1,son[j],w[j]); 64 }else 65 if (son[j]==1){ 66 if (pre[i]==i) addn(i,n+1,w[j]); else addn(1,n+1,w[j]+dis[son[j]]); 67 }else{ 68 if (pre[i]!=pre[son[j]]) addn(1,son[j],dis[i]+w[j]); else addn(i,son[j],w[j]); 69 } 70 spfa_new(); 71 if (dis[n+1]!=dis[0]) printf("%d",dis[n+1]); else puts("-1"); 72 return 0; 73 }View Code
福慧雙修(both)