T2 洞穴zaw
【问题描述】
在 Byte 山的山脚下有一个洞穴入口. 这个洞穴由复杂的洞室经过隧道连接构成. 洞穴的入口是 1 号点.两个洞室要么就通过隧道连接起来,要么就经过若干隧道间接的相连. 现在决定组织办一个‘King‘s of Byteotia Cup‘ 比赛. 参赛者的目标就是任意选择一条路径进入洞穴并尽快出来即可. 一条路径必须经过除了 1 之外还至少要经过其他一个洞室.一条路径中一个洞不能重复经过(除了 1 以外),类似的一条隧道也不能重复经过.
一个著名的洞穴探险家 Byteala 正准备参加这个比赛. Byteala 已经训练了数月而且他已获得了洞穴系统的一套详细资料. 对于每条隧道他都详细计算了从两个方向经过所需要的时间. 经过一个洞室的时间很短可以忽略不记. 现在Byteala 向计算一条符合条件的最优路径.
【输入格式】
第一行有两个数 n 和 m (3 <= n <= 5000, 3 <= m <= 10000) 分别表示洞室的数目以及连接他们的隧道的数目. 洞室从 1 到 n 编号. “前面洞室”的编号为 1.接下来 m 行描述了所有的隧道. 每行四个整数 a,b,c,d 表示从洞室 a 到洞室 b 需要 c 分钟的时间,而从洞室 b 到洞室 a 需要 d 分钟的时间, 1 <= a,b <= n, a <> b, 1 <=c,d <= 10000. 你可以假设符合要求的路径肯定存在.
【输出格式】
输出一行,最少需要多少时间完成比赛.
【样例输入】
3 3
1 2 4 3
2 3 4 2
1 3 1 1
【样例输出】
6
【说明】
经过 1,2,3,1
Solution
来源:POI2004,Bzoj2069
①O(n^2logn)
从1点出发枚举会走到哪条边哪到哪个点,将这条边回边标为不可选,从那个点跑向1的最短路即可.
因为有些奇奇怪怪的剪枝,它会跑的非常快.
剪枝:
- 普通单源单汇的最短路剪枝,Dijskra算法,当前更新的最小点为汇点可直接输出
- 枚举的那条边>=ans直接不枚举
- 当前最小点+W[i](枚举的那条边)>=ans可以退出
- 到达的点>=ans可以不入队
②%DJ两遍最短路 nlogn
一条边相当与四条边,为什么呢?双向×走法(直+倒)
edge u→v w[1]=w1(直) w[2]=w2(倒)
edge v→u w[1]=w2(直) w[2]=w1(倒)
我们拿每条边w[1](标记v→w不能)从1跑最短路,第二遍用w[2]跑,这样是从1倒着走回到其他点,相当与从其它点走过来.
统计一下每个点两次答案之和即可.
Code
①
// <zaw.cpp> - Thu Oct 6 08:17:54 2016 // This file is made by YJinpeng,created by XuYike‘s black technology automatically. // Copyright (C) 2016 ChangJun High School, Inc. // I don‘t know what this program is. #include <iostream> #include <vector> #include <algorithm> #include <cstring> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #define INF 1e9 #define IN inline #define RG register using namespace std; typedef long long LL; inline int gi() { register int w=0,q=0;register char ch=getchar(); while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘)ch=getchar(); if(ch==‘-‘)q=1,ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘)w=w*10+ch-‘0‘,ch=getchar(); return q?-w:w; } const int N=5010,M=N<<2; int n,m,t,ans;int d[N],fr[N];int to[M],ne[M],W[M];bool u[N]; struct node{ int s,p; bool operator<(node a)const{return s>a.s;} }; priority_queue<node>q; IN void link(RG int u,RG int v,RG int w){ to[++t]=v;ne[t]=fr[u];fr[u]=t;W[t]=w; //if(u==1)cout<<v<<endl;this } void read(){ n=gi(),m=gi(); while(m--){ int u=gi(),v=gi(),w=gi(),w1=gi(); link(u,v,w);link(v,u,w1); } } IN int Dij(RG int begin,RG int end,int w){ for(int i=1;i<=n;i++)d[i]=INF; q.push((node){d[begin]=0,begin}); memset(u,0,sizeof(u)); while(!q.empty()){ while(u[q.top().p]&&!q.empty())q.pop(); if(q.empty())break; int x=q.top().p;q.pop();u[x]=1; if(w+d[x]>=ans)break;//this if(x==end)return d[1]; for(int o=fr[x],y;y=to[o],o;o=ne[o]) if(d[x]+W[o]<d[y]){ d[y]=d[x]+W[o]; if(d[y]>=ans)continue;//this q.push((node){d[y],y}); } } return d[1]; } void Work(){ read();ans=INF; for(int i=fr[1],w;i;i=ne[i]){ if(W[i]>=ans)continue;//this if(i&1)w=W[i+1],W[i+1]=INF;else w=W[i-1],W[i-1]=INF; ans=min(ans,W[i]+Dij(to[i],1,W[i])); if(i&1)W[i+1]=w;else W[i-1]=w; } printf("%d",ans); } int main() { freopen("zaw.in","r",stdin); freopen("zaw.out","w",stdout); Work(); return 0; }
②