福慧双修(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数组来搞事情了:
a.如果有一条从1到x的边,边权为w:
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 }