Description&Data Constraint
对于全部数据,(2le N le 200,1le M le 5 imes 10^4,1le U_i,V_ile N,U_i ot= V_i,0le C_ile 10^6,0le D_ile 10^9)。
Solution
首先明确一个性质,翻转的边需要在最短路树上,否则对答案没有贡献。
首先求出 (1) 到 (i),(i) 到 (n) 的最短路径,并把在最短路树上的边打上标记。
也求出 (n) 到 (i),(i) 到 (1) 的最短路径,同时打上最短路树的标记。
具体操作可以建正图和反图共 (4) 个,两个正图,两个反图,正图和反图中各有一个以 (1) 为起点,一个以 (n) 为起点,以不同的起点求出对应的最短路。
约定:
-
以 (1) 为起点的正图为图 (1)。目的是求出 (1) 到 (i) 的最短路。
-
以 (n) 为起点的反图为图 (2)。目的是求出 (i) 到 (n) 的最短路。
-
以 (n) 为起点的正图为图 (3)。目的是求出 (n) 到 (i) 的最短路。
-
以 (1) 为起点的反图为图 (4)。目的是求出 (i) 到 (1) 的最短路。
-
一条边的起点是 (x),终点是 (y),权值是 (v)。
令初始答案为在不翻转边下的 (1) 到 (n) 和 (n) 到 (1) 的最短路径和。
考虑枚举边,对于 (1) 到 (n),如果该边不在图的最短路树上,就返回原值,否则将此边翻转跑最短路。
对于翻转,显然不能找到原边将其翻转。有一种想法是给原边打上标记,加入新的翻转边并在最短路结束后将其删掉。
另一种想法是手动走翻转边。对于一条边,要么走要么不走。不走就是在没有原边的基础上跑最短路,走则是先从 (1) 走到 (y),再从 (x) 走到 (n),同时补上中间少了的 (v)。
将 (1) 到 (n) 和 (n) 到 (1) 分开处理,求出两个最短路,判断最短路径和加上修改当前边的权值与答案的大小,更新答案。
最后判断 (-1),输出答案即可。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 1e9
#define N 205
#define M 50005
#define ll long long
using namespace std;
int n,m,x[M],y[M];
ll ans,z[M],d[M];
struct gra
{
int s,tot,cant,last[M];
ll dis1[N],dis2[N];
bool bin[M],bj[N];
struct node
{
int to,next,head;
ll val;
}a[M];
void add(int x,int y,ll z)
{
a[++tot].to=y;
a[tot].val=z;
a[tot].next=a[x].head;
a[x].head=tot;
}
void dij()
{
memset(bj,false,sizeof(bj));
for (int i=1;i<=n+1;++i)
dis1[i]=inf;
dis1[s]=0;
for (int i=1;i<=n;++i)
{
int k=n+1;
for (int j=1;j<=n;++j)
if (!bj[j]&&dis1[j]<dis1[k]) k=j;
if (k==n+1) break;
bj[k]=true;
for (int j=a[k].head;j;j=a[j].next)
{
int u=a[j].to;
if (!bj[u]&&dis1[u]>dis1[k]+a[j].val) dis1[u]=dis1[k]+a[j].val,last[u]=j;
}
}
for (int i=1;i<=n;++i)
if (i!=s) bin[last[i]]=true;
}
void get()
{
memset(bj,false,sizeof(bj));
for (int i=1;i<=n+1;++i)
dis2[i]=inf;
dis2[s]=0;
for (int i=1;i<=n;++i)
{
int k=n+1;
for (int j=1;j<=n;++j)
if (!bj[j]&&dis2[j]<dis2[k]) k=j;
if (k==n+1) break;
bj[k]=true;
for (int j=a[k].head;j;j=a[j].next)
{
int u=a[j].to;
if (!bj[u]&&dis2[u]>dis2[k]+a[j].val&&j!=cant) dis2[u]=dis2[k]+a[j].val;
}
}
}
ll calc(int e,int ss)
{
if (!bin[e]) return dis1[ss];
cant=e;
get();
return dis2[ss];
}
}g[10];
int main()
{
scanf("%d%d",&n,&m);
g[1].s=g[4].s=1;
g[2].s=g[3].s=n;
for (int i=1;i<=m;++i)
{
scanf("%d%d%lld%lld",&x[i],&y[i],&z[i],&d[i]);
g[1].add(x[i],y[i],z[i]);g[2].add(y[i],x[i],z[i]);
g[3].add(x[i],y[i],z[i]);g[4].add(y[i],x[i],z[i]);
}
for (int i=1;i<=4;++i)
g[i].dij();
ans=g[1].dis1[n]+g[3].dis1[1];
for (int i=1;i<=m;++i)
{
ll d1=min(g[1].calc(i,n),g[1].calc(i,y[i])+z[i]+g[2].calc(i,x[i]));
ll d2=min(g[3].calc(i,1),g[3].calc(i,y[i])+z[i]+g[4].calc(i,x[i]));
ans=min(ans,d1+d2+d[i]);
}
if (ans>=1e9) printf("-1
");
else printf("%lld
",ans);
return 0;
}