• LibreOj #539. 「LibreOJ NOIP Round #1」旅游路线


    题目链接

    做完这道题,我深知当一个问题复杂度过高的时候,把一些可以分离的操作都分散开,可以大幅度降低复杂度.....


    发现无论有多少钱,每到一个点后扩展到的距离被限制在 (min(C, c[i]))边内,故可对此设计 (DP)

    由于 (D) 很大,不妨将其设为 (DP) 的价值,用的钱设置为容量。

    所以我们只需要枚举那些需要加油的点,用最优性取跳即可。

    Step 1: 快速求出从 (u)(v) 不超过 (c[i]) 条边的最大距离

    (g[u][v][k]) 表示从 (u) 走到 (v) 不超过 (2 ^ k) 条边走的最远距离。

    注意,这里 (K) 的最大值是 (log_2C),因为最多扩展 (C) 条边。

    (O(N^3K)) 可以预处理来这个玩意,递推式:

    初始状态 (g[u][v][0] = d[u][v])

    (g[u][v][k] = max(g[u][x][k - 1] + g[x][v][k - 1]))


    (w[u][v]) 表示从 (u) 跑到 (v) 不超过 (min(C, c[i])) 条边的最长距离。

    即在 (u) 加油后跑到 (v) 的最长距离。

    这个东西可以枚举 (min(C, c[i])) 的二进制位,用多个 (1) 拼起来。

    具体转移式:

    (w[u][v] = max(last[u][x] + g[x][v][k]))


    Step 2:大力转移!

    (f[i][j]) 为从 (i) 出发,用不超过 (j) 块钱能扩展到的最大距离。

    状态转移方程:

    (f[u][j] = max(w[u][v]))

    (f[u][q] = max(w[u][x] + f[x][q - p[x]]))


    Step 3:Ans!

    显然,对于一个 (u)(f[u][j] (0 <= j <= q)) 是递增序列的。

    那么我们需要找到一个尽量小的 (j),使得 (f[u][j] >= d)

    用二分不就行了?。

    时间复杂度 (O(N^3log_C + N ^ 4 + T(log_2N^2)))

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    using namespace std;
    const int N = 105, M = 1005, L = 17;
    int n, m, C, T, g[N][N][L];
    int w[N][N], tmp[N], f[N][N * N];
    int p[N], c[N];
    int main() {
        memset(g, -0x3f, sizeof g);
        memset(w, -0x3f, sizeof w);
        scanf("%d%d%d%d", &n, &m, &C, &T);
        for (int i = 1; i <= n; i++) g[i][i][0] = 0;
        for (int i = 1; i <= n; i++) scanf("%d%d", p + i, c + i), c[i] = min(c[i], C);
        for (int i = 1, u, v, w; i <= m; i++) {
            scanf("%d%d%d", &u, &v, &w);
            g[u][v][0] = max(g[u][v][0], w);
        }
    
        for (int k = 1; k < L; k++) 
            for (int u = 1; u <= n; u++)
                for (int v = 1; v <= n; v++)
                    for (int x = 1; x <= n; x++)
                        g[u][v][k] = max(g[u][v][k], g[u][x][k - 1] + g[x][v][k - 1]);
        
    
        for (int u = 1; u <= n; u++) {
            bool flag = true;
            for (int k = 0; k < L; k++) {
                if(c[u] >> k & 1) {
                    if(flag) {
                        for (int v = 1; v <= n; v++) {
                            w[u][v] = tmp[v] = g[u][v][k];
                        }
                        flag = false;
                        continue;
                    }
    
                    for (int v = 1; v <= n; v++)
                        for (int x = 1; x <= n; x++)
                            w[u][v] = max(w[u][v], tmp[x] + g[x][v][k]);
    
                    for (int v = 1; v <= n; v++) tmp[v] = w[u][v];
                }
            }
        } 
    
        for (int q = 0; q <= n * n; q++) {
            for (int u = 1; u <= n; u++) {
                for (int v = 1; v <= n; v++) {
                    f[u][q] = max(f[u][q], w[u][v]);
                    if(q >= p[v]) f[u][q] = max(f[u][q], w[u][v] + f[v][q - p[v]]);
                }
            }
        }
        
       
        
        for (int i = 1, s, q, d; i <= T; i++) {
            scanf("%d%d%d", &s, &q, &d);
            int l = p[s], r = q;
            if(r < l || f[s][r - l] < d) {
                puts("-1"); continue;
            }
            while(l < r) {
                int mid = (l + r) >> 1;
                if(f[s][mid - p[s]] >= d) r = mid;
                else l = mid + 1;
            }
            printf("%d
    ", q - r);
        }
        return 0;
    }
    
  • 相关阅读:
    [转载]Android之NetworkOnMainThreadException异常
    Android学习笔记(一): Fragment(一) 基本概念和生命周期
    ubuntu 64位系统创建android 项目找不到R文件
    Cocos2d之“引用计数”内存管理机制实现解析
    Cocos2d之Ref类与内存管理使用详解
    JSON之HelloWord
    Mysql之HelloWorld
    Linux inittab 配置文件
    计算机端口号对应服务总结
    perl文件读写
  • 原文地址:https://www.cnblogs.com/dmoransky/p/11723088.html
Copyright © 2020-2023  润新知