Description
Solution
這裏暴力都能過!!!
先不過這些。
我們要找一條路徑出來,首先肯定是過1號節點連出去的邊的。
我們可以枚舉這個邊,然後求出起點不經過這條邊的最短路徑到達j,然後再加上這條邊的權值。
怎麼算不經過這條邊的最短路徑呢?
我們可以維護最短路徑和次短路徑,如果這條邊被最短路徑包括了,那麼就走次短路徑,否則直接走最短路徑。但是我們要保證最短路徑的出邊和次短路徑的出邊不相等(出邊就是起點走出來的第一條邊),否則可能會把出邊重複走,因爲最短如果包含出邊的話,那麼出邊就是枚舉的那條邊,次短路明顯不能包含這條邊。
維護最短和次短,且要求出邊不同實際上很簡單。用最短更新最短,最短更新次短,次短更新次短。
Code
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=2e5+7;
int i,j,k,l,t,n,m,ans,x,y,u,v,r,mid;
int first[maxn*2],last[maxn*2],next[maxn*2],chang[maxn*2],num,fan[maxn*2];
int d[maxn],cd[maxn],dy[maxn],cdy[maxn],data[maxn];
bool bz[maxn*2];
void add(int x,int y,int z,int u){
last[++num]=y,next[num]=first[x],first[x]=num,chang[num]=z;fan[num]=num+1;
last[++num]=x,next[num]=first[y],first[y]=num,chang[num]=u;fan[num]=num-1;
}
void spfa(){
int head=0,tail=1,i,j,now;data[1]=1;
memset(d,127,sizeof(d));memset(cd,127,sizeof(cd));d[1]=0;bz[1]=1;
while(head<tail){
now=data[++head];
rep(i,now){
if(d[last[i]]>d[now]+chang[i]){
d[last[i]]=d[now]+chang[i];dy[last[i]]=dy[now];
if(now==1)dy[last[i]]=i;
if(!bz[last[i]]){
bz[last[i]]=1;
data[++tail]=last[i];
}
}
else if(cd[last[i]]>d[now]+chang[i]&&dy[now]!=dy[last[i]]&&fan[dy[now]]!=dy[last[i]]&&last[i]!=1){
cd[last[i]]=d[now]+chang[i];cdy[last[i]]=dy[now];
if(now==1)cdy[last[i]]=i;
if(!bz[last[i]]){
bz[last[i]]=1;
data[++tail]=last[i];
}
}
if(cd[last[i]]>cd[now]+chang[i]&&cdy[now]!=dy[last[i]]&&fan[cdy[now]]!=dy[last[i]]&&last[i]!=1){
cd[last[i]]=cd[now]+chang[i];cdy[last[i]]=cdy[now];
if(now==1)cdy[last[i]]=i;
if(!bz[last[i]]){
bz[last[i]]=1;
data[++tail]=last[i];
}
}
}
bz[now]=0;
}
}
int main(){
// freopen("fan.in","r",stdin);
scanf("%d%d",&n,&m);
fo(i,1,m)scanf("%d%d%d%d",&x,&y,&u,&v),add(x,y,u,v),r+=max(u,v);
spfa();ans=0x7fffffff;
rep(i,1){
if(d[last[i]]!=d[1]+chang[i]){
ans=min(ans,d[last[i]]+chang[fan[i]]);
}
else ans=min(ans,cd[last[i]]+chang[fan[i]]);
}
printf("%d\n",ans);
}