[AHOI2017初中组]guide
题目描述
农场主John最近在网上买了一辆新车,在购买汽车配件时,John不小心点了两次“提交”按钮。导致汽车上安装了两套GPS系统,更糟糕的是John在使用GPS导航时,两套系统常常给出不同的路线。从地图上看,John居住的地区有N(2 ≤ N ≤ 100,000)个十字路口和M(1 ≤ M ≤ 500,000)条限定通行方向的道路。第i条道路连接路口 A_i (1 ≤ A_i ≤ N)和B_i (1 ≤ B_i ≤ N),两个路口之间可能连接有多条道路。允许双向通⾏的道路是将两条单向通⾏的道路隔开所形成的。
John的家在路口1位置,农场在路口N的位置。John可以沿着⼀系列单向道路从家驾车到农场。所有GPS系统的底层地图信息都是⼀样的,区别仅在于对每一条道路的通⾏时间计算不同。对于第i条道路第一套GPS系统计算通行时间为P_i个单位时间,而第二套GPS系统则给出Q_i个单位时间。(所有道路的通行时间都是范围在1到100,000之间的整数)John想要驾车从家到农场。可是,一路上GPS系统总是不厌其烦的提醒John(请从路口X开往路口Y),这是由于John选取了某套GPS系统建议的路径,而另一套GPS系统则认为这不是从路口X到农场的最短路径。我们称之为GPS系统的抱怨。
请你计算一下如果John选择合适的路径到达农场能听到的最少GPS系统的抱怨数 。如果John经过某条道路两套GPS系统都发出抱怨,则抱怨总数加2。
输入格式
第一行,两个整数N和M。
接下来M行,其中第i行描述了道路i的信息,A_i B_i P_i Q_i。
输出格式
一个整数,表示John一路上能听到的最小抱怨数。
样例 #1
样例输入 #1
5 7 3 4 7 1
1 3 2 20
1 4 17 18
4 5 25 3
1 2 10 1
3 5 4 14
2 4 6 5
样例输出 #1
1
这道题乍一看很难,我们来仔细分析一下
本题说的是John有两个GPS系统,给了n个点和m条边。唯一独特的是,两个GPS所给的路途时间不同,所以从点1到点n的最短路径不同。如果John从当点走的路线与两个GPS给的从当点到终点的最短路径不同,则GPS会抱怨(一个不同抱怨一下,两个的话两下),求最少抱怨总数。
怎么判断走这条边会抱怨几次呢?
我们无论怎么走,都是要从此点沿最短路径走到终点,不妨我们都用最短路径算法求出两个GPS从点1n-1到点n的最短路径(就是以点n为源点,求到所有点的最短路径,反过来就是从点1n-1到点n的最短路径了。这里要注意,反着求的时候,边的方向要反过来,否则求得的结果是错误的!!)
由于极限情况是100000个点,只能选择效率高的算法,例如SPFA,或者我写的dijkstra堆优化版。
求得两个GPS从前n-1个点到终点的最短路径后,判断走这条边,是否抱怨,方法不难:枚举每条边,沿着这条边走时,若原来该点的最短路径值 - 该边的权值(即时间)=下一点的最短路径时,说明该边是最短路径上的一条边,不会抱怨;若不等于,则说明该边不是最短路径上的边,该边抱怨次数+1。于是我们得到了每条边抱怨的次数。
最后再以抱怨次数为边的权值,求一下从起点到终点的最短路径,即为最少抱怨数。
时间复杂度:O(nlogn);空间复杂度:O(m);//堆及数组的开支为m
顺便bb几句,这也是我人生中的第二道蓝题。。本来码完一遍试了一下样例,样例过了就直接交了一发,没想到直接一遍A了,,高兴了一整个晚上,,开开心心的下机了。。。。
#include <atomic>
#include <bits/stdc++.h>
using namespace std;
/*
本来只是过了样例想试一发的
结果能一遍过我是没想到的
回来打份解说。。
GPS会给你当前点位到n的最短路径
所以存反向边来跑最短路
这样就能得到每个点到n的最短距离
由于同一条边两个GPS给的边长不等
所以分开求两份最短路
存在两个dist数组中
得到两张最短路之后
枚举已经存好的每条边
如果dist[start] + p[i] != dist[end]
那么说明GPS将会抱怨
因为在GPS眼中,这条边并不是最佳方案
两个GPS对于这条边的抱怨总次数
就成为了这条边新的边权
利用这个边权
能够得到一张全新的最短路
*/
typedef long long LL;
typedef pair<LL, int> PLI;
const int N = 100005, M = 500005;
int n, m;
int s[M], f[M];
int com[M];
int h[N], e[M], ne[M], idx;
LL p[M], q[M];
LL dist_1[N], dist_2[N], dist_3[N];
bool st[N];
void add(int a, int b, int x, int y)
{
s[idx] = a, f[idx] = b; //记录起点终点
e[idx] = b, ne[idx] = h[a], p[idx] = x, q[idx] = y, h[a] = idx ++;
}
void dijkstra_1()
{
memset(dist_1, 0x3f, sizeof dist_1);
memset(st, 0, sizeof st);
priority_queue<PLI, vector<PLI>, greater<PLI> > heap;
dist_1[n] = 0;
heap.push({0, n});
while(heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second;
LL distance = t.first;
if(st[ver]) continue;
st[ver] = true;
for(int i = h[ver]; i != -1; i = ne[i]) {
int j = e[i];
if(distance + p[i] < dist_1[j])
{
dist_1[j] = distance + p[i];
heap.push({dist_1[j], j});
}
}
}
}
void dijkstra_2()
{
memset(dist_2, 0x3f, sizeof dist_2);
memset(st, 0, sizeof st);
priority_queue<PLI, vector<PLI>, greater<PLI> > heap;
dist_2[n] = 0;
heap.push({0, n});
while(heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second;
LL distance = t.first;
if(st[ver]) continue;
st[ver] = true;
for(int i = h[ver]; i != -1; i = ne[i]) {
int j = e[i];
if(distance + q[i] < dist_2[j])
{
dist_2[j] = distance + q[i];
heap.push({dist_2[j], j});
}
}
}
}
void dijkstra_3()
{
memset(dist_3, 0x3f, sizeof dist_3);
memset(st, 0, sizeof st);
priority_queue<PLI, vector<PLI>, greater<PLI> > heap;
dist_3[n] = 0;
heap.push({0, n});
while(heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second;
LL distance = t.first;
if(st[ver]) continue;
st[ver] = true;
for(int i = h[ver]; i != -1; i = ne[i]) {
int j = e[i];
if(distance + com[i] < dist_3[j])
{
dist_3[j] = distance + com[i];
heap.push({dist_3[j], j});
}
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
memset(h, -1, sizeof h);
cin >> n >> m;
for(int i = 1; i <= m; i ++) {
int a, b, x, y;
cin >> a >> b >> x >> y;
add(b, a, x, y); //建反向图
}
dijkstra_1();
dijkstra_2();
for(int i = 0; i < idx; i ++) {
if(p[i] + dist_1[s[i]] > dist_1[f[i]]) com[i] ++;
if(q[i] + dist_2[s[i]] > dist_2[f[i]]) com[i] ++;
} //枚举所有边
dijkstra_3();
cout << dist_3[1];
return 0;
}