算法标签:最短路
难度:提高+/省选-
本题说的是John有两个GPS系统,给了n个点和m条边。唯一独特的是,两个GPS所给的路途时间不同,所以从点1到点n的最短路径不同。如果John从当点走的路线与两个GPS给的从当点到终点的最短路径不同,则GPS会抱怨(一个不同抱怨一下,两个的话两下),求最少抱怨总数。
怎么判断走这条边会抱怨几次呢?
我们无论怎么走,都是要从此点沿最短路径走到终点,不妨我们都用最短路径算法求出两个GPS从点1->n-1到点n的最短路径(就是以点n为源点,求到所有点的最短路径,反过来就是从点1->n-1到点n的最短路径了。这里要注意,反着求的时候,边的方向要反过来,否则求得的结果是错误的!!)
由于极限情况是100000个点,只能选择效率高的算法,例如SPFA,或者我写的dijkstra堆优化版。
求得两个GPS从前n-1个点到终点的最短路径后,判断走这条边,是否抱怨,方法不难:枚举每条边,沿着这条边走时,若原来该点的最短路径值 - 该边的权值(即时间)=下一点的最短路径时,说明该边是最短路径上的一条边,不会抱怨;若不等于,则说明该边不是最短路径上的边,该边抱怨次数+1。于是我们得到了每条边抱怨的次数。
最后再以抱怨次数为边的权值,求一下从起点到终点的最短路径,即为最少抱怨数。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define N 100010
#define M 500010
#define INF 1e12
#define LL long long
int n,m,cnt,h[N],v[N];
LL d[3][N];
struct Node
{
int to;
LL w[3];
}e;
vector <Node> g[N];
queue <int> q;
int read ()
{
int s=0,k=1;char ch=getchar ();
while (!isdigit (ch)) {if (ch=='-') k=-1;ch=getchar ();}
while (isdigit (ch)) {s=s*10+ch-'0';ch=getchar ();}
return k*s;
}
void Init ()
{
n=read ();m=read ();
for (int i=1;i<=m;i++)
{
int x=read (),y=read ();
LL w1=read (),w2=read ();
e.to=x;e.w[0]=w1;e.w[1]=w2;
g[y].push_back (e);
}
for (int i=1;i<=n;i++)
d[0][i]=d[1][i]=d[2][i]=INF;
}
void SPFA (int s,int mode)
{
memset (v,0,sizeof (v));
q.push (s);d[mode][s]=0;v[s]=1;
while (!q.empty ())
{
int x=q.front ();
q.pop ();v[x]=0;
for (int i=0;i<g[x].size ();i++)
{
int y=g[x][i].to;
LL w=g[x][i].w[mode];
if (d[mode][x]+w<d[mode][y])
{
d[mode][y]=d[mode][x]+w;
if (!v[y])
{
q.push (y);
v[y]=1;
}
}
}
}
}
void Work ()
{
SPFA (n,0);
SPFA (n,1);
for (int x=1;x<=n;x++)
for (int i=0;i<g[x].size ();i++)
{
int y=g[x][i].to;
LL w1=g[x][i].w[0],w2=g[x][i].w[1];
if (d[0][y]!=d[0][x]+w1) g[x][i].w[2]++;
if (d[1][y]!=d[1][x]+w2) g[x][i].w[2]++;
}
SPFA (n,2);
printf ("%lld\n",d[2][1]);
}
int main ()
{
Init ();
Work ();
return 0;
}