题解:洛谷 P9948 Race

【题目来源】

洛谷:P9948 [USACO20JAN] Race B - 洛谷

【题目描述】

Bessie 正在参加一场 \(K(1\le K\le 10^9)\)米的跑步比赛。她从 \(0\) 米每秒的速度开始比赛。在每一秒中,她可以选择将她的速度增加\(1\) 米每秒,保持速度不变,或者将她的速度减少 \(1\) 米每秒。例如,在第一秒中,她可以将她的速度增加到 \(1\)米每秒,跑 \(1\) 米,或者保持她的速度\(0\)米每秒不变,跑 \(0\) 米。Bessie 的速度不会降低到小于零。

Bessie 始终朝着终点线的方向跑,她想要花费整数秒的时间完成比赛。此外,她不想在终点时跑得太快:在 Bessie 跑完 \(K\)米的时刻,她希望她的速度不超过 \(X(1\le X\le 10^5)\)米每秒。Bessie 想要对于\(N(1\le N\le 1000)\)个不同的\(X\) 值知道她多快可以完成比赛。

【输入】

输入的第一行包含两个整数 \(K\)\(N\)

以下\(N\) 行每行包含一个整数 \(X\)

【输出】

输出 \(N\) 行,每行包含一个整数,表示 Bessie 完成比赛时的速度小于或等于 \(X\) 的情况下跑完 \(K\)米需要的最小时间。

【输入样例】

10 5
1
2
3
4
5

【输出样例】

6
5
5
4
4

【核心思想】

  1. 问题分析:给定总路程 \(K\)\(1 \le K \le 10^9\)),起点速度0,每秒速度可变化 \(-1,0,+1\),且速度非负。要求在跑完 \(K\) 米的结束时刻速度不超过给定的 \(X\),求最小整数秒数。对于 \(N\) 个不同的 \(X\) 分别作答(\(N \le 1000\))。本质是一个带终点速度约束的加速-减速最优控制问题,需要最小化时间,等价于在速度限制下最大化每秒平均速度,且速度曲线呈“先加速后减速”的对称结构。

  2. 算法选择

    • 数学推导 + 二分答案:对于给定的 \(X\),最优策略是先以 \(+1\) 加速到某个峰值 \(V\),然后以 \(-1\) 减速到终点速度恰好为 \(X\)(或 \(X+1\),根据奇偶性调整)。跑过的总路程是速度曲线下的面积。可以通过二分搜索“峰值速度以上额外运行的时间”来求解。
    • 分段计算:将过程分为三段:
      1. 从0加速到 \(X\)(速度从0到 \(X\),耗时 \(X\) 秒,路程 \(\frac{X(X+1)}{2}\))。
      2. 以速度 \(X\) 为基底,额外增加一段“等腰梯形”或“三角形”的变速过程(速度从 \(X\) 上升到峰值再回到 \(X\)),该过程增加的路程为 \(m \cdot X + \text{额外面积}\),其中 \(m\) 是额外的时间秒数。
      3. \(X\) 减速到 \(X\)(实际是回到 \(X\),形成闭合)后的路程计算。
  3. 关键步骤(基于代码逻辑):

    • 预处理:读入 \(K\)\(N\),对每个询问 \(X\) 单独计算。
    • 情况1:仅加速段即可完成:计算从0加速到 \(X\) 的路程 \(base = \frac{X(X+1)}{2}\)。若 \(base \ge K\),则只需加速(可能未达到 \(X\) 就已跑完),最小时间 \(t = \lceil \sqrt{2K + 0.25} - 0.5 \rceil\),即满足 \(\frac{v(v+1)}{2} \ge K\) 的最小整数 \(v\)
    • 情况2:需要加速到峰值再减速
      • \(remain = K - base\) 为需要额外跑的路程。
      • 二分搜索额外的“往返时间” \(m\)(即在速度 \(X\) 之上运行的总秒数)。\(m\) 的最小可能值为0,最大可能值为 \((K - base) / X\)(取上整)。
      • 对于给定的 \(m\),计算在 \(m\) 秒内,速度从 \(X\) 出发,经过加速再减速回到 \(X\) 能提供的最大额外路程 \(extraMax\)。最优结构是:若 \(m\) 为偶数 \(2a\),则先加速 \(a\) 秒再减速 \(a\) 秒,额外路程为 \(a(a+1)\);若 \(m\) 为奇数 \(2a+1\),则先加速 \(a\) 秒,保持峰值 \(1\) 秒,再减速 \(a\) 秒,额外路程为 \((a+1)^2\)
      • 二分找到最小的 \(m\),使得 \(extraMax \ge remain\)
      • 总时间 \(ans = X + m\)(因为 \(base\) 部分耗时 \(X\) 秒,\(m\) 为额外秒数)。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log K)\),其中 \(N \le 1000\),二分范围约 \(K/X\),但 \(K \le 10^9\)\(\log K\) 约 30,完全可行。
    • 空间复杂度:\(O(1)\)
  5. 贪心 + 二分最优速度曲线的核心思想

    • 最优策略的对称性:在终点速度限制下,为了最短时间完成固定路程,速度曲线应尽可能“高”且“平坦”。因此,最优方案是单调加速到某个峰值,再单调减速到终点速度 \(X\)(速度变化率为 ±1)。这形成了一个关于峰值对称的梯形(或三角形)。
    • 面积分解:将总路程分解为“基础加速段”(0→X)和“额外振荡段”(X→峰值→X)。基础段的时间和路程固定,只需最小化额外段的时间,这转化为给定额外时间 \(m\) 求最大额外路程的单调函数,从而可二分。
    • 整数约束处理:速度和路程均为整数,二分边界和公式计算需小心取整。
    • 适用场景:适用于一维运动学优化,加速度受限(仅 ±1)且终点速度有上界,求最短时间。核心是利用速度-时间图的面积(路程)进行数学推导和二分。

【解题思路】

【算法标签】

普及 #整数二分

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int k, n, x;
// ifstream filein("race.in");
// ofstream fileout("race.out");
int main()
{
    // filein >> k >> n;  // 输入k和n
    cin >> k >> n;  // 输入k和n
    while (n--) {  // 遍历n次询问
        // filein >> x;  // 输入x
        cin >> x;  // 输入x
        long long presum = (long long)(x+1)*x/2;  // 求出presum(细节处理,需要加上long long)
        if (presum>=k) {  // 如果presum已经超过了k
            // fileout << ceil(sqrt(2*k+0.25)-0.5) << endl;  // 反向求
            cout << ceil(sqrt(2*k+0.25)-0.5) << endl;  // 反向求
            continue;
        }
        int l=0, r=(k-presum)/x;  // 二分左值为0,右值为(k-presum)/x
        if ((k-presum)%x!=0) {  // 如果r不为整数,自增1(细节处理)
            r++;
        }
        while (l<r-1) {  // 二分查找
            int m = (l+r)/2;  // 中间点
            int tot = k-presum-x*m;  // tot为增加面积。(tot+presum+m*x)>= k
            long long cmax=0;  // 定义cmax,后面需要与tot比较
            int up=m/2;  // up取m的一半
            if (m%2==0) {
                cmax = (long long)up*up;  // 增加的面积
            } else {
                cmax = (long long)(1+up)*up;  // 增加的面积(细节处理)
            }
            if (cmax>=tot) {  // 如果cmax大于等于tot,说明m设大了
                r = m;
            } else {  // 否则说明设小了
                l = m;
            }
        }
        // fileout << x+r << endl;  // x是presum的时间,r为m这段的时间
        cout << x+r << endl;  // x是presum的时间,r为m这段的时间
    }
    return 0;
}

【运行结果】

10 5
1
6
2
5
3
5
4
4
5
4
posted @ 2026-08-30 09:21  团爸讲算法  阅读(10)  评论(0)    收藏  举报