【备战秋招】每日一题:2023.08.19-小红书-第三题-塔子哥旅游

题目内容

塔子哥很喜欢旅游,目标是游遍全球。作为一个程序猿,塔子哥想写个程序来规划自己的旅游路线。

现在塔子哥来到了一个城市,这个城市有 n n n 个景点,有 m m m 条路连通这 n n n 个景点。游览景点 i i i 花费的时间为 t i t_i ti ,获得的快乐值为 h i h_i hi

i i i 条路连通 u i u_i ui v i v_i vi 这两个景点( u i ≠ v i u_i\neq v_i ui=vi),通过这条路花费的时间为 w i w_i wi

塔子哥精力有限,所以至多只会游览 3 3 3 个景点,且这 3 3 3 个景点是相邻的,一天游览和交通所需的时间不能超过 k k k

现在,塔子哥想问你,在给定的时间下,通过游览景点可以获得的最大快乐值是多少。

输入描述

第一行,输入三个整数 n , m , k ( 1 ≤ n , m ≤ 1 0 5 , 1 ≤ k ≤ 1 0 9 ) n, m, k(1\leq n, m\leq 10^5, 1\leq k\leq 10^9) n,m,k(1n,m105,1k109),表示景点数,路径数和最大交通时间。

第二行输入 n n n 个整数,表示每个景点的快乐值 h i ( 1 ≤ h i ≤ 1 0 9 ) h_i(1\leq h_i\leq 10^9) hi(1hi109)

第三行输入 n n n 个整数,表示游览每个景点的时间 t i ( 1 ≤ t i ≤ 1 0 9 ) t_i(1\leq t_i\leq 10^9) ti(1ti109)

接下来 m m m 行,第 i i i 行三个数 u i , v i , w i ( 1 ≤ u i , v i ≤ n , 1 ≤ w i ≤ 1 0 9 ) u_i, v_i, w_i(1\leq u_i, v_i\leq n, 1\leq w_i\leq 10^9) ui,vi,wi(1ui,vin,1wi109) 表示两个景点和通过这条路径花费的时间。

输出描述

一个整数,表示在给定的时间下,通过游览景点可以获得的最大快乐值。

样例

输入

4 3 10
10 9 8 7
1 2 3 4
1 2 1
2 3 1
3 4 10

输出

27

思路:枚举 + 二分

这题至多游览三个景点,而且要求任意两个游览的景点,至多通过两条路径可以到达。

考虑游览的景点数:

  • 一个,遍历所有景点即可
  • 两个,枚举每条边即可
  • 三个,枚举每个点 x x x 的所有邻居,根据通过路径的时间和游览景点的时间从小到大排序。然后继续枚举点 x x x 的邻居 y y y ,再二分得到符合条件的邻居 z z z 即可。

时间复杂度: O ( m log ⁡ m ) O(m\log m) O(mlogm)

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<ll, ll> PLL;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m, k;
    cin >> n >> m >> k;

    vector<ll> h(n), t(n);
    for (int i = 0; i < n; ++i) cin >> h[i];
    for (int i = 0; i < n; ++i) cin >> t[i];

    // 建图
    vector<vector<PLL>> g(n);
    for (int i = 0; i < m; ++i) {
        int u, v;
        ll w;
        cin >> u >> v >> w;
        u--; v--;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }

    ll ans = 0;
    // 一个点
    for (int i = 0; i < n; ++i) {
        if (t[i] <= k) {
            ans = max(ans, h[i]);
        }
    }

    // 两个点
    for (int i = 0; i < n; ++i) {
        for (auto& [j, w]: g[i]) {
            if (w + t[i] + t[j] <= k) {
                ans = max(ans, h[i] + h[j]);
            }
        }
    }

    // 三个点
    for (int i = 0; i < n; ++i) {
        vector<PLL> vec;
        for (auto& [j, w]: g[i]) {
            // 选择每个点的时间为 w + t[j],获得的快乐值为 h[j]
            vec.emplace_back(w + t[j], h[j]);
        }

        // 按照选择每个点需要耗费的时间为第一关键字排序
        sort(vec.begin(), vec.end());

        // 第 i 个点作为中间点,再选择其两个邻居景点

        // 此时,我们可以枚举第 x 个点作为第一个邻居点,1 到 x - 1 中选择一个合适的点作为第二个邻居点
        // 我们对于时间先排序,那么我们可以知道的是,从 1 到 x - 1 中,二分出一个总时间小于等于 k ,价值尽可能大的第二邻居点
        for (int x = 1; x < int(vec.size()); ++x) {
            int l = 0, r = x - 1;
            while (l < r) {
                int mid = (l + r + 1) >> 1;
                if (vec[mid].first + vec[x].first + t[i] <= k) {
                    l = mid;
                } else {
                    r = mid - 1;
                }
            }

            if (vec[l].first + vec[x].first + t[i] <= k) {
                ans = max(ans, h[i] + vec[x].second + vec[l].second);
            }
            vec[x].second = max(vec[x].second, vec[x - 1].second);
        }
    }

    cout << ans << "\n";

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

塔子哥学算法

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

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

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

打赏作者

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

抵扣说明:

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

余额充值