题解:洛谷 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
【核心思想】
-
问题分析:给定赛车初始力 \(F\)、质量 \(M\),以及 \(N\) 个零件(每个提供力 \(F_i\)、质量 \(M_i\))。目标是最大化力质量比 \(\frac{F + \sum F_i}{M + \sum M_i}\),在此基础上最小化总质量 \(M + \sum M_i\)。这是一个贪心问题,核心在于按零件的力质量比排序,选择能提升整体力质量比的零件。
-
算法选择:
- 贪心策略:按零件力质量比 \(r_i = F_i/M_i\) 降序排序,依次选择 \(r_i \geq\) 当前赛车力质量比的零件
- 单调性保证:每次加入 \(r_i \geq\) 当前比值的零件,整体比值不会下降
-
关键步骤:
- 初始化:读取 \(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\)
- 若 \(r_i \geq F/M\)(当前赛车力质量比):
- 输出:按编号升序排序 \(ans\),若为空输出
NONE
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\),排序 \(O(N \log N)\),遍历 \(O(N)\)
- 空间复杂度:\(O(N)\),存储零件信息
-
贪心策略的核心思想:
- 力质量比排序:优先选择"单位质量提供力"最高的零件,这是提升整体比值的关键
- 单调性条件:仅当零件比值不低于当前整体比值时才选择,确保加入后整体比值不下降(加权平均的性质:加入一个不低于当前平均值的数,平均值不会下降)
- 质量最小化:在满足最大比值的前提下,按比值排序后贪心选择的方案自然使总质量最小(因为比值高的零件质量效率更高,用更少的质量获得更大的比值提升)
- 唯一解保证:题目保证解唯一,排序后贪心选择的顺序确定
- 适用于比率优化、加权平均、贪心选择类问题
【算法标签】
普及 #贪心
【代码详解】
#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
浙公网安备 33010602011771号