E. Rendez-vous de Marian et Robin题解

题目链接;这篇文章提供了一个比隔壁某天ye更简单的思路和更好理解的代码(bushi);

题意分析

这题很明显是一个最短路的问题,并且没有负边,所以首选Dijkstra算法,不同于裸的Dijkstra算法,这里多了两个奇妙的条件:

  1. 当某个节点出现马的时候,后续路程的花费减半;
  2. 两个人分别从1号点和n号点同时出发,并且在某个点相遇(即不可以在边上相遇);

处理条件

到达新的节点

对于出现取到一个新的节点的时候,有3种情况:
1. 一直没有马;
2. 从没有到有;
3. 一直有马;
而对于一直没有马的情况,显然可以用Dijkstra处理来得到到这个节点的最短距离(就是一个裸的Dijkstra模版);而这里的难点就是:如何处理从无马到有马的这种情况呢==>再堆中新增一个参数来传递上一个节点是否存在马就可以了!;

    priority_queue<pair<int, pair<int, bool>>, vector<pair<int, pair<int, bool>>>, greater<pair<int, pair<int, bool>>>>
        pq;
auto t = pq.top();
pq.pop();

t.first;-->代表边的距离;
t.second.first;-->代表这个边的终点是那个点;
t.second.second;-->代表到达这个点的--之前--是否有马;

这样处理之后每次每次只需要在加入队列的时候只需要更新一下就可以了–>即继承上一个节点的马或者是该节点有新的马;
所以只需要正常开一个int dist[N];再编写一个裸的Dijkstra就可以了-----吗?

接着往下看看:

马状态的转移

若有这样一张图:3个节点,2条边,在2号点有一匹马;
分别是1–>2,边权是4;1–>3,边权是16;
那么我们从1号点到达3号点的最短距离用裸的Dijkstra处理出来会是怎么样的呢?(这个地方因为可能会往回走同一段路,所以不做标记)

  1. 找到最短的边1–>2,并做一个标记,dist[2]=4;
  2. 找到下一条边2–>1,但是1号点的距离是0,所以不会更新;
  3. 找到最后一条边1–>3,花费是16;

所以最终的花费就是16;
但是实际情况是什么呢?到达2号点之后获得了一匹马,再返回1号点,在从1号点出发去3号点,这样的花费仅之后14!所以裸的Dijkstra是不可靠的!

我们可以这么处理:分开处理无马和有马两次Dijkstra的结果,并且处理好状态转移:即先跑无马的Dijkstra,等到遇到马的时候,从无马的Dijkstra继承距离,并且以这个点为起点再重新开始一个Dijkstra!

同时出发并且在某个点相遇

这个思路就很简单了,就是遍历每可能相遇的点i,从1-->in-->i所花费的最大值就是在i点相遇所需的时间(因为只能在点上相遇,并且其中一个人可以在一个点上等待);
而这里重要的知识点是Lamda函数的使用(真的和好用awa)

        int res = INF;
        // 重要的是这个赋值和捕获!
        auto get = [&](int a)
        {
            return max(min(dist1[a][0], dist1[a][1]), min(dist2[a][0], dist2[a][1]));
        };
        for (int i = 1; i <= n; i++)
            res = min(res, get(i));
        cout << (res == INF ? -1 : res) << '\n';

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
// 开long long !!!
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
const int INF = 1e18;

int m, n, h;

void dijkstra(vector<vector<PII>>& g, vector<vector<int>>& dist, vector<bool>& hs, int start)
{
    dist[start][0] = 0;

    vector<vector<bool>> st(n + 1, vector<bool>(2, false));

    priority_queue<pair<int, pair<int, bool>>, vector<pair<int, pair<int, bool>>>, greater<pair<int, pair<int, bool>>>>
        pq;
    pq.push(make_pair(0, make_pair(start, 0)));

    while (pq.size())
    {
        auto t = pq.top();
        pq.pop();

        int ver = t.second.first;

        bool house = (t.second.second || hs[t.second.first]);

        if(st[ver][house])
            continue;
        st[ver][house] = true;

        for (auto [j, w] : g[ver])
        {
            int finW = house ? w / 2 : w;
            if (dist[j][house] > dist[t.second.first][t.second.second] + finW)
            {
                dist[j][house] = dist[t.second.first][t.second.second] + finW;
                pq.push({dist[j][house], {j, house}});
            }
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        cin >> n >> m >> h;

        vector<bool> hs(n + 1);
        while (h--)
        {
            int c;
            cin >> c;
            hs[c] = true;
        }

        vector<vector<PII>> g(n + 1);
        while (m--)
        {
            int a, b, w;
            cin >> a >> b >> w;
            // 无向图存储
            g[a].push_back(make_pair(b, w));
            g[b].push_back(make_pair(a, w));
        }
        vector<vector<int>> dist1(n + 1, vector<int>(2, INF));
        vector<vector<int>> dist2(n + 1, vector<int>(2, INF));


        dijkstra(g, dist1, hs, 1);
        dijkstra(g, dist2, hs, n);

        int res = INF;
        auto get = [&](int a)
        {
            return max(min(dist1[a][0], dist1[a][1]), min(dist2[a][0], dist2[a][1]));
        };
        for (int i = 1; i <= n; i++)
            res = min(res, get(i));
        cout << (res == INF ? -1 : res) << '\n';
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值