题解:洛谷 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

【核心思想】

  1. 问题分析:FJ 需要从坐标 \(0\) 走到 \(E\),沿途在 \(N\) 家商店购买 \(K\) 吨饲料。运输费用与载重和距离成正比(\(D \times X\)),购买费用为每吨 \(C_i\)。求最小总花费。本质上是贪心排序问题:将购买费用和运输费用合并为"实际单位费用",按实际费用从小到大贪心购买。

  2. 算法选择

    • 实际费用计算:在位置 \(X_i\) 购买 \(1\) 吨饲料并运到终点 \(E\) 的总费用 = 购买费 \(C_i\) + 运输费 \((E - X_i)\)
    • 贪心排序:按实际单位费用 tot = C_i + (E - X_i) 升序排序,优先购买费用低的商店
  3. 关键步骤

    • 读取数据:读入 \(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\),结束
    • 输出 \(sum\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),排序主导
    • 空间复杂度:\(O(N)\),存储商店信息
  5. 贪心排序的核心思想

    • 费用合并:将购买和运输两种费用合并为"从该商店购买并运到终点的单位总费用",问题转化为简单的资源采购
    • 贪心最优性:实际费用低的商店优先购买,保证每单位饲料的花费最小,全局最优
    • 库存约束:每家商店有库存上限 \(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
posted @ 2026-08-17 09:46  团爸讲算法  阅读(10)  评论(0)    收藏  举报