1 条题解

  • 0
    @ 2025-5-4 17:21:06

    分析:

    本题要找出到达第 n 个检查点的最短时间,可将其拆分为到达每个检查点的子问题。通过考虑在不同的前序检查点更换轮胎,计算出到达每个检查点的最短时间,最终得到到达第 n 个检查点的最短时间。

    解题原理

    核心算法思想是动态规划。通过定义状态 dp[i] 表示到达第 i 个检查点的最短时间,利用子问题的最优解来推导当前问题的最优解。在计算 dp[i] 时,枚举所有可能的前一个检查点 j,计算从 j 到 i 的行驶时间,加上到达 j 的最短时间以及可能的换轮胎时间,取最小值作为 dp[i] 的值。

    实现步骤

    1.预计算时间表:根据不同距离和速度规则,预先计算出从起点到每个距离的行驶时间,存储在 time_table 数组中。

    2.初始化动态规划数组:将 dp 数组初始化为一个很大的值,dp[0] 设为 0,表示起点的时间为 0。

    3.动态规划计算:

    1.遍历每个检查点 i(从 1 到 n)。

    2.对于每个 i,枚举所有可能的前一个检查点 j(从 0 到 i - 1)。

    3.计算从 j 到 i 的行驶时间,通过 time_table 数组直接获取。

    4.计算到达 i 的总时间,包括到达 j 的最短时间、从 j 到 i 的行驶时间以及可能的换轮胎时间。

    5.更新 dp[i] 为所有可能路径中的最小值。

    4.输出结果:输出 dp[n],即到达第 n 个检查点的最短时间。

    c++代码:

    #include <stdio.h>
    #define MAX_N 110
    #define MAX_DIST 10010
    
    double dp[MAX_N];          // dp[i] 表示到达第 i 个检查点的最短时间
    int a[MAX_N];              // 检查点距离
    double time_table[MAX_DIST]; // 预计算的时间表
    int n;
    double b, r, v, e, f;
    
    // 预计算时间表
    void precompute_time_table(int max_dist) {
        time_table[0] = 0.0;
        for (int i = 1; i <= max_dist; i++) {
            double denominator;
            if (i - 1 >= r) {
                denominator = v - e * ((i - 1) - r);
            } else {
                denominator = v - f * (r - (i - 1));
            }
            if (denominator < 1e-6) {
                fprintf(stderr, "分母过小,可能导致计算异常,程序终止。\n");
                return;
            }
            if (i - 1 >= r) {
                time_table[i] = time_table[i - 1] + 1.0 / (v - e * ((i - 1) - r));
            } else {
                time_table[i] = time_table[i - 1] + 1.0 / (v - f * (r - (i - 1)));
            }
        }
    }
    
    void solve() {
        // 初始化 dp 数组
        for (int i = 0; i <= n; i++) {
            dp[i] = 1e20;  // 初始化为很大的值
        }
        dp[0] = 0.0;  // 起点时间为 0
    
        // 动态规划计算最优解
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < i; j++) {
                // 从 j 到 i 不换轮胎的距离
                int dist = a[i - 1] - (j == 0 ? 0 : a[j - 1]);
                // 直接查表获取行驶时间
                double segment_time = time_table[dist];
                // 如果 j > 0,需要考虑换轮胎时间 b
                double total_time = dp[j] + segment_time + (j > 0 ? b : 0);
                if (total_time < dp[i]) {
                    dp[i] = total_time;
                }
            }
        }
    
        printf("%.4f\n", dp[n]);
    }
    
    int main() {
        while (scanf("%d", &n) == 1 && n != 0) {
            if (n < 1 || n > MAX_N) {
                fprintf(stderr, "检查点数量n超出合法范围,程序终止。\n");
                return 1;
            }
            // 读取检查点距离
            for (int i = 0; i < n; i++) {
                scanf("%d", &a[i]);
            }
            // 读取其他参数
            scanf("%lf", &b);
            scanf("%lf", &r);
            scanf("%lf", &v);
            scanf("%lf", &e);
            scanf("%lf", &f);
    
            if (a[n - 1] >= MAX_DIST) {
                fprintf(stderr, "检查点距离超过预定义的最大距离,程序终止。\n");
                return 1;
            }
            precompute_time_table(a[n - 1]);
    
            solve();
        }
        return 0;
    }
    
    • 1

    信息

    ID
    1744
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    10
    已通过
    2
    上传者