题解:洛谷 P2989 Need For Speed

【题目来源】

洛谷:P2989 [USACO10MAR] Need For Speed S

【题目描述】

Bessie 正在为即将到来的赛车比赛作准备。

她有一辆赛车,质量为 \(M\),且可以提供 \(F\) 的力。现在她想要给这辆赛车安装一些零件(总共有 \(N\) 个零件),每个零件具有属性 \(M_i\)\(F_i\),表示其质量以及可以提供的力。

\(X_i = 1\)\(0\),表示第 \(i\) 个零件选或不选。在最大化

\[\dfrac{F+\sum_{i=1}^{n}X_i \cdot F_i}{M+\sum_{i=1}^{n}X_i \cdot M_i} \]

的前提下最小化

\[\sum_{i=1}^{n}X_i \cdot M_i + M. \]

【输入】

第一行是三个用空格分开的正整数 \(F,\,M,\,N\)

接下来 \(N\) 行,每行两个用空格分开的正整数,第 \(i+1\) 行的两个数代表 \(F_i\)\(M_i\)

【输出】

输出包含 \(P\) 行,表示 Bessie 需要安装的 \(P\) 个零件的下标。若 Bessie 不需要给这辆车安装零件,输出 NONE

输出应按递增顺序给出,如果最佳零件集为 \(\{2,4,6,7\}\),则输出应按 \(2,4,6,7\) 的顺序,而不是 \(4,2,7,6\) 的顺序。解决方案将是唯一的。

【输入样例】

1500 100 4
250 25
150 9
120 5
200 8

【输出样例】

2
3
4

【核心思想】

  1. 问题分析:给定赛车初始力 \(F\)、质量 \(M\),以及 \(N\) 个零件(每个提供力 \(F_i\)、质量 \(M_i\))。目标是最大化力质量比 \(\frac{F + \sum F_i}{M + \sum M_i}\),在此基础上最小化总质量 \(M + \sum M_i\)。这是一个贪心问题,核心在于按零件的力质量比排序,选择能提升整体力质量比的零件。

  2. 算法选择

    • 贪心策略:按零件力质量比 \(r_i = F_i/M_i\) 降序排序,依次选择 \(r_i \geq\) 当前赛车力质量比的零件
    • 单调性保证:每次加入 \(r_i \geq\) 当前比值的零件,整体比值不会下降
  3. 关键步骤

    • 初始化:读取 \(F\)\(M\)\(N\)\(N\) 个零件的 \((F_i, M_i)\)
    • 计算比值\(r_i = F_i / M_i\),记录原始编号
    • 排序:按 \(r_i\) 降序排序
    • 贪心选择(遍历排序后的零件):
      • \(r_i \geq F/M\)(当前赛车力质量比):
        • 选中该零件,\(ans.push\_back(id)\)
        • 更新 \(F += F_i\)\(M += M_i\)
    • 输出:按编号升序排序 \(ans\),若为空输出 NONE
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),排序 \(O(N \log N)\),遍历 \(O(N)\)
    • 空间复杂度:\(O(N)\),存储零件信息
  5. 贪心策略的核心思想

    • 力质量比排序:优先选择"单位质量提供力"最高的零件,这是提升整体比值的关键
    • 单调性条件:仅当零件比值不低于当前整体比值时才选择,确保加入后整体比值不下降(加权平均的性质:加入一个不低于当前平均值的数,平均值不会下降)
    • 质量最小化:在满足最大比值的前提下,按比值排序后贪心选择的方案自然使总质量最小(因为比值高的零件质量效率更高,用更少的质量获得更大的比值提升)
    • 唯一解保证:题目保证解唯一,排序后贪心选择的顺序确定
    • 适用于比率优化、加权平均、贪心选择类问题
      【算法标签】

普及 #贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 10005; // 最大零件数量
int F, M, n; // F:赛车初始提供的力, M:赛车初始质量, n:可选零件数量
struct Node
{
    int id, f, m; // id:零件编号, f:零件提供的力, m:零件的质量
    double r; // r:零件的力质量比 Fi/Mi
}a[N];
vector<int> ans; // 存储选中的零件编号

// 按力质量比从大到小排序
bool cmp(Node x, Node y)
{
    return x.r > y.r;
}

int main()
{
    cin >> F >> M >> n; // 读入初始力、初始质量和零件数量
    double maxn = -1; // (未使用,保留原代码风格)
    for (int i=1; i<=n; i++) // 读入每个零件的属性
    {
        cin >> a[i].f >> a[i].m;
        a[i].id = i; // 记录原始编号
        a[i].r = 1.0 * a[i].f / a[i].m; // 计算力质量比
    }
    sort(a+1, a+n+1, cmp); // 按力质量比降序排序

    // 贪心策略:依次选择力质量比不低于当前赛车力质量比的零件
    // 这样可以保证每次加入零件后,整体力质量比不会下降
    for (int i=1; i<=n; i++)
    {
        // 如果当前零件的力质量比不低于当前赛车的力质量比
        if (a[i].r >= 1.0 * F / M)
        {
            ans.push_back(a[i].id); // 选中该零件
            F = F + a[i].f; // 更新赛车的总力
            M = M + a[i].m; // 更新赛车的总质量
        }
    }

    // 按原始编号递增顺序输出选中的零件
    sort(ans.begin(), ans.end());
    if (ans.size()==0) // 如果没有选中任何零件
        cout << "NONE" << endl;
    else
        for (auto t : ans)
            cout << t << endl;
    return 0;
}

【运行结果】

1500 100 4
250 25
150 9
120 5
200 8
2
3
4
posted @ 2026-08-13 14:14  团爸讲算法  阅读(9)  评论(0)    收藏  举报