Fork me on GitHub

2025.8.11校队分享:旅游路线

题面

题目传送门

做法

我们从答案往后倒推,如果我们要求最多可以剩下多少钱,发现 \(T \le 10^5\) 那一定就是预处理每个点有 \(k\) 元钱能走多远, 我们就设 \(f_{i,cost}\) 来表示从 \(i\) 开始走,花 \(cost\) 最多能走多远,我们就可以有一个转移:

\(f_{i,cost} = \max(f_{i,cost}, f_{j, cost - p[i]} + 从 i 处加油开始走到 j 的最远距离)\)

给一下代码:

    for(int cost = 0 ; cost <= n * n ; cost ++ )
        for(int i = 1 ; i <= n ; i ++ )
            if(p[i] <= cost)
                for(int j = 1 ; j <= n ; j ++ )
                    f[i][cost] = max(f[i][cost], f[j][cost - p[i]] + g[i][j]);

所以我们要再列一个辅助数组 \(g_{i,j}\) 来表示在 \(i\) 处加油后走到 \(j\) 的最远距离,我们还是没法一下子就求出 \(g\), 又得再列几个辅助辅助转移数组的数组(bushi,考虑按位处理 \(c_i\) 对于每一位用倍增距离来维护转移,可能有点懵,我们把他抽象化:对于每一位的 \(c_i\) 设立两个子辅助数组 \(tmp1\)\(tmp2\)\(tmp2\) 表示还没有更新完的 \(g_i\)\(tmp1\) 表示更新以后的 \(tmp2\) , 显然有 \(tmp1_j = \max(tmp1_j, tmp2_k + dist_{k, j, u})\) 转移一轮完 \(tmp2 = tmp1\) 最终转移完 \(g_i = tmp2\), 其中 \(dist_{i, j, u}\) 表示从 \(i\)\(j\) 走了 \(2^u\) 条边的最长距离。

这是维护 \(g\) 的代码:

for(int i = 1 ; i <= n ; i ++ )
    {
        for(int j = 1 ; j <= n ; j ++ ) tmp2[j] = tmp1[j] = -1e18;
        tmp2[i] = 0;
        for(int u = 18 ; u >= 0 ; u -- )
            if(c[i] & (1 << u))
            {
                for(int j = 1 ; j <= n ; j ++ )
                    for(int k = 1 ; k <= n ; k ++ )
                    tmp1[j] = max(tmp1[j], tmp2[k] + dist[k][j][u]);
                for(int j = 1 ; j <= n ; j ++ ) tmp2[j] = tmp1[j];
            }
        for(int j = 1 ; j <= n ; j ++ ) g[i][j] = tmp2[j];
    }

最后,你会发现 \(dist\) 已经不用再用其他的数组来维护了,直接倍增Floyd就好了, 下放代码:

    for(int u = 0 ; u <= 18 ; u ++ )
        for(int i = 1 ; i <= n ; i ++ )
            for(int j = 1 ; j <= n ; j ++ )
                dist[i][j][u] = -1e18;
    for(int i = 1 ; i <= n ; i ++ ) dist[i][i][0] = 0;
    for(int i = 1 ; i <= m ; i ++ )
    {
        int a, b, l;
        cin >> a >> b >> l;
        dist[a][b][0] = max(dist[a][b][0], l);
    }
    for(int u = 1 ; u <= 18 ; u ++ )
        for(int k = 1 ; k <= n ; k ++ )
            for(int i = 1 ; i <= n ; i ++ )
                for(int j = 1 ; j <= n ; j ++ )
                    dist[i][j][u] = max(dist[i][k][u - 1] + dist[k][j][u - 1], dist[i][j][u]);

这道题目就是很经典的倒推思想,从答案推到给出的信息,倒着推很丝滑,但如果你正着推就无从下手,以上只是我的看法,大佬们可能还会有其他的更简单的,或者是可以从正着想的方法,反正我不会 TAT

最后的完整代码 QwQ

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 110;
const int M = N * N;
const int K = 30;

int n, m, C, T;
int dist[N][N][K];
int tmp1[N], tmp2[N];
int p[N], c[N];
int f[N][M];
int g[N][N];

signed main()
{
    cin >> n >> m >> C >> T;
    for(int i = 1 ; i <= n ; i ++ ) cin >> p[i] >> c[i];
    for(int i = 1 ; i <= n ; i ++ ) c[i] = min(C, c[i]);
    for(int u = 0 ; u <= 18 ; u ++ )
        for(int i = 1 ; i <= n ; i ++ )
            for(int j = 1 ; j <= n ; j ++ )
                dist[i][j][u] = -1e18;
    for(int i = 1 ; i <= n ; i ++ ) dist[i][i][0] = 0;
    for(int i = 1 ; i <= m ; i ++ )
    {
        int a, b, l;
        cin >> a >> b >> l;
        dist[a][b][0] = max(dist[a][b][0], l);
    }
    for(int u = 1 ; u <= 18 ; u ++ )
        for(int k = 1 ; k <= n ; k ++ )
            for(int i = 1 ; i <= n ; i ++ )
                for(int j = 1 ; j <= n ; j ++ )
                    dist[i][j][u] = max(dist[i][k][u - 1] + dist[k][j][u - 1], dist[i][j][u]);
    
    for(int i = 1 ; i <= n ; i ++ )
    {
        for(int j = 1 ; j <= n ; j ++ ) tmp2[j] = tmp1[j] = -1e18;
        tmp2[i] = 0;
        for(int u = 18 ; u >= 0 ; u -- )
            if(c[i] & (1 << u))
            {
                for(int j = 1 ; j <= n ; j ++ )
                    for(int k = 1 ; k <= n ; k ++ )
                    tmp1[j] = max(tmp1[j], tmp2[k] + dist[k][j][u]);
                for(int j = 1 ; j <= n ; j ++ ) tmp2[j] = tmp1[j];
            }
        for(int j = 1 ; j <= n ; j ++ ) g[i][j] = tmp2[j];
    }
    
    for(int cost = 0 ; cost <= n * n ; cost ++ )
        for(int i = 1 ; i <= n ; i ++ )
            if(p[i] <= cost)
                for(int j = 1 ; j <= n ; j ++ )
                    f[i][cost] = max(f[i][cost], f[j][cost - p[i]] + g[i][j]);
    while(T -- )
    {
        int s, q, d;
        cin >> s >> q >> d;
        bool st = false;
        for(int i = 0 ; i <= q + 1 ; i ++ )
            if(f[s][i] >= d)
            {
                cout << q - i << endl;
                st = true;
                break;
            }
        if(st == false) cout << "-1\n";
    }
    return 0;
}
posted @ 2025-08-11 12:22  tony0530  阅读(30)  评论(0)    收藏  举报