题解:洛谷 P2616 Buying Feed, II
【题目来源】
洛谷:P2616 [USACO10JAN] Buying Feed, II S - 洛谷
【题目描述】
FJ 开车来到镇上,他要带 \(K\) 吨饲料回家。运送饲料是需要花钱的,如果他的车上有 \(X\) 吨饲料,开车 \(D\) 公里就需要 \(D×X\) 元。FJ 可以从 \(N\) 家商店购买饲料,所有商店都在一个坐标轴上,第 \(i\) 家店的位置是 \(X_i\),饲料的售价为每吨 \(C_i\) 元,库存为 \(F_i\)。
这个镇上有 \(N(1 \le N \le 100)\) 家商店(编号为 \(1 \sim N\))售卖饲料,所有商店都在一个长度为 \(E(1 \le E \le 350)\) 的 \(X\) 轴上。第 \(i\) 个商店位于数轴上 \(X_i\) 的位置,最多可以售卖给 FJ \(F_i(1 \le F_i \le 100)\) 吨饲料,花费为 \(C_i(1 \le C_i \le 10^6)\) 元每吨。奇妙的是,\(X\) 轴上同一个坐标可能不只有一家商店。
FJ 从坐标为 \(0\) 的地方出发并且只能向前走,直到到达坐标为 \(E\) 的地方,并且需要买到 \(K\) 吨饲料。他可以在沿途任意一家商店停下来买饲料。
请你求出 FJ 购买并运输 \(K\) 吨饲料的最小花费是多少。
【输入】
- 第一行:三个整数 \(K\),$ E $ 和 $ N \(,\) 1\leq K \leq 100 \(,\) 1\leq E \leq 350 \(,\) 1\leq N \leq 100 $;
- 第二行到第 $ N + 1 $ 行:第 $ i + 1 $ 行有三个整数 $ X_i \(,\) F_i $ 和 $ C_i $,$0 < X_i < E $,$1\leq F_i \leq 100 $,\(1\leq C_i \leq 10^6\)。
【输出】
一个整数,表示购买并运送饲料的最小花费。
【输入样例】
2 5 3
3 1 2
4 1 2
1 1 1
【输出样例】
7
【核心思想】
-
问题分析:FJ 需要从坐标 \(0\) 走到 \(E\),沿途在 \(N\) 家商店购买 \(K\) 吨饲料。运输费用与载重和距离成正比(\(D \times X\)),购买费用为每吨 \(C_i\)。求最小总花费。本质上是贪心排序问题:将购买费用和运输费用合并为"实际单位费用",按实际费用从小到大贪心购买。
-
算法选择:
- 实际费用计算:在位置 \(X_i\) 购买 \(1\) 吨饲料并运到终点 \(E\) 的总费用 = 购买费 \(C_i\) + 运输费 \((E - X_i)\)
- 贪心排序:按实际单位费用
tot = C_i + (E - X_i)升序排序,优先购买费用低的商店
-
关键步骤:
- 读取数据:读入 \(K, E, N\) 和 \(N\) 家商店的信息 \((X_i, F_i, C_i)\)
- 计算实际费用:\(p[i].tot = C_i + (E - X_i)\)
- 排序:按
tot升序排列 - 贪心购买(\(i = 1\) 到 \(N\)):
- 若 \(K > F_i\):全部购买,
sum += tot * F_i,\(K \leftarrow K - F_i\) - 否则:购买剩余 \(K\) 吨,
sum += tot * K,\(K = 0\),结束
- 若 \(K > F_i\):全部购买,
- 输出 \(sum\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\),排序主导
- 空间复杂度:\(O(N)\),存储商店信息
-
贪心排序的核心思想:
- 费用合并:将购买和运输两种费用合并为"从该商店购买并运到终点的单位总费用",问题转化为简单的资源采购
- 贪心最优性:实际费用低的商店优先购买,保证每单位饲料的花费最小,全局最优
- 库存约束:每家商店有库存上限 \(F_i\),可能需要在多家商店分散购买
- 终点运输的隐含性:所有饲料最终都要运到 \(E\),因此在 \(X_i\) 购买的饲料必然运输 \((E - X_i)\) 距离
- 适用于资源采购、费用合并、贪心选择类问题
【解题思路】

【算法标签】
普及- #贪心
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int k, e, n, sum=0;
struct node {
int x, f, c, tot;
}p[105];
bool cmp(node x, node y)
{
return x.tot<y.tot;
}
int main()
{
cin >> k >> e >> n; // 输入k、e和n
for (int i=1; i<=n; i++) { // 遍历n个商店
cin >> p[i].x >> p[i].f >> p[i].c; // 输入其x、f和c
p[i].tot = p[i].c + (e-p[i].x); // 并计算实际的费用
}
sort(p+1, p+n+1, cmp); // 按照tot从小到大排序
for (int i=1; i<=n; i++){ // 遍历n个商店
if (k>p[i].f) { // 如果k大于某个商店的份数
sum += p[i].tot * p[i].f; // 则将这个商店的所有份数加上
k -= p[i].f; // 更新k
} else { // 否则说明商店的份数多了
sum += p[i].tot * k; // 则只算上某商店k份的实际费用
k = 0; // 更新k
break; // 并不再计算
}
}
cout << sum << endl; // 计算费用总和
return 0;
}
【运行结果】
2 5 3
3 1 2
4 1 2
1 1 1
7
浙公网安备 33010602011771号