最短路--P3720 [AHOI2017初中组]guide

12 篇文章 0 订阅

[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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Silver_Bullet14

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值