福慧双修(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数组来搞事情了:
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 }

时间: 2024-10-07 15:18:16

福慧双修(both)的相关文章

bzoj 4398 福慧双修 - 最短路

Description 菩萨为行,福慧双修,智人得果,不忘其本.——唐朠立<大慈恩寺三藏法师传>有才而知进退,福慧双修,这才难得.——乌雅氏如何福慧双修?被太后教导的甄嬛徘徊在御花园当中.突然,她发现御花园中的花朵全都是红色和蓝色的.她冥冥之中得到了响应:这就是指导她如何福慧双修的! 现在御花园可以看作是有N块区域,M条小路,两块区域之间可通过小路连接起来.现在甄嬛站在1号区域,而她需要在御花园中绕一绕,且至少经过1个非1号区 域的区域.但是恰好1号区域离碎玉轩最近,因此她最后还是要回到1号区

bzoj4398:福慧双修

学习了一下最短路的姿势,这个建图方法好妙啊,虽然不会证明正确性…… #include <bits/stdc++.h> #define N 220000 #define INF 1000000000 using namespace std; int n, m; int ai[N], bi[N], ci[N], di[N]; int check; class DIJ { private: struct node {int t, d;}; struct comp {int operator () (

bzoj4398: 福慧双修

正边权无向图,一条边两个方向权值不一定相同,求经过点1的最小简单环 简单环包含了点1的一条出边和一条入边,且这两条边不同,因此可以枚举这两条边的编号的二进制表示中哪一位不同,用最短路求此时的最优解,时间复杂度$O(mlog^2m)$ #include<bits/stdc++.h> using std::swap; using std::vector; using std::priority_queue; const int N=40007,inf=1000000000; int _(){ in

【BZOJ2407/4398】探险/福慧双修 最短路建模

[BZOJ2407]探险 Description 探险家小T好高兴!X国要举办一次溶洞探险比赛,获奖者将得到丰厚奖品哦!小T虽然对奖品不感兴趣,但是这个大振名声的机会当然不能错过! 比赛即将开始,工作人员说明了这次比赛的规则:每个溶洞和其他某些溶洞有暗道相连.两个溶洞之间可能有多条道路,也有可能没有,但没有一条暗道直接从自己连到自己.参赛者需要统一从一个大溶洞出发,并再次回到这个大溶洞. 如果就这么点限制,那么问题就太简单了,可是举办方又提出了一个条件:不能经过同一条暗道两次.这个条件让大家犯难

【视频+文字】刘素云老师:法雨惠群生--老实念佛不拐弯 今生一定到彼岸(附净空老法师点评)

刘素云老师 常念阿彌陀佛 2015-09-08 法雨惠群生 ——老实念佛不拐弯 今生一定到彼岸 2015年9月4日上午十点首播的<无量寿经科注第四回学习班>第165集 视频(建议WIFI下收看)   2015年9月4日上午十点首播的<无量寿经科注第四回学习班>第165集 从二O一四年五月到二O一五年五月,整整一年的时间,海贤老和尚的光碟我听(看)了两千遍.可能有的同修会说,不就是告诉念阿弥陀佛吗,用得着听(看)那么多遍吗?以我的切身体会,我的回答是,用得着,不但是用得着,而且是非常

《认识佛教》有声书-02-净空法师-初学佛课程(音)

说法狮子吼 2016-11-18 認識佛教有聲書02来自说法狮子吼00:0027:39 阿罗汉与辟支佛用的心,好像是水里的月亮,镜子里的月亮,叫镜花水月,都不是真的.从用心上看,菩萨的心跟佛的心很相似.很接近,是真的,不是假的:所以纵然是初住菩萨,刚刚破一品无明,证一分法身,他用的心就是真心,绝对没有虚妄,这叫‘正等正觉’.修学得到正等正觉,这个学位称为‘菩萨’.阿罗汉好比是学士,菩萨好比是硕士,佛好比是博士.所以‘佛’是通称,不是释迦牟尼佛一个人独称:任何人智慧达到究竟圆满,就是真心圆证,就称

不会的图论题

列一下曾经做过的不会的图论题 bzoj4398: 福慧双修 bzoj4681: [Jsoi2010]旅行 NEERC 2017 Problem J Journey from Petersburg to Moscow 原文地址:https://www.cnblogs.com/tkandi/p/10556829.html