题解:洛谷 P1208 混合牛奶

【题目来源】

洛谷:P1208 [USACO1.3] 混合牛奶 Mixing Milk - 洛谷

【题目描述】

由于乳制品产业利润很低,所以降低原材料(牛奶)价格就变得十分重要。帮助 Marry 乳业找到最优的牛奶采购方案。

Marry 乳业从一些奶农手中采购牛奶,并且每一位奶农为乳制品加工企业提供的价格可能相同。此外,就像每头奶牛每天只能挤出固定数量的奶,每位奶农每天能提供的牛奶数量是一定的。每天 Marry 乳业可以从奶农手中采购到小于或者等于奶农最大产量的整数数量的牛奶。

给出 Marry 乳业每天对牛奶的需求量,还有每位奶农提供的牛奶单价和产量。计算采购足够数量的牛奶所需的最小花费。

注:每天所有奶农的总产量大于 Marry 乳业的需求量。

【输入】

第一行二个整数 \(n,m\),表示需要牛奶的总量,和提供牛奶的农民个数。

接下来 \(m\) 行,每行两个整数 \(p_i,a_i\),表示第 \(i\) 个农民牛奶的单价,和农民 \(i\) 一天最多能卖出的牛奶量。

【输出】

单独的一行包含单独的一个整数,表示 Marry 的牛奶制造公司拿到所需的牛奶所要的最小费用。

【输入样例】

100 5
5 20
9 40
3 10
8 80
6 30

【输出样例】

630

【核心思想】

  1. 问题分析:给定牛奶总需求量 \(n\)\(m\) 个奶农,每个奶农有单价 \(p_i\) 和最大产量 \(a_i\)。要求采购至少 \(n\) 单位牛奶的最小总花费。这是一个经典的贪心选择问题——在满足需求的前提下,优先选择单价最低的奶农。

  2. 算法选择

    • 按单价排序贪心:将所有奶农按单价从小到大排序,依次购买直到满足需求量
    • 局部最优即全局最优:每次选择当前最便宜的奶农,由于单价独立且可拆分购买,贪心策略必然得到全局最优解
  3. 关键步骤

    • 读入数据\(n\)(需求量)、\(m\)(奶农数),以及每个奶农的单价 \(p_i\) 和产量 \(a_i\)
    • 按单价升序排序:使用自定义比较函数将奶农数组按 \(p\) 从小到大排列
    • 贪心采购(遍历排序后的奶农):
      • 若剩余需求量 \(n \geq a_i\):全额购买该奶农的牛奶,ans += p_i * a_in -= a_i
      • 若剩余需求量 \(n < a_i\):只购买剩余需求量,ans += p_i * n,结束循环
    • 输出结果:最小总花费 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(m \log m)\),排序主导;采购遍历 \(O(m)\)
    • 空间复杂度:\(O(m)\),存储奶农信息数组
  5. 贪心策略的核心思想

    • 单价优先原则:在可拆分购买的情况下,优先消耗低价资源必然使总成本最小
    • 需求递减追踪:维护剩余需求量 \(n\),每购买一批就减少对应数量,直至满足需求
    • 部分购买合法性:题目允许购买小于等于最大产量的任意整数数量,因此最后一家可以只买剩余部分
    • 排序保证顺序性:按单价排序后,前面的奶农一定比后面的便宜,无需回头检查
    • 适用于资源采购、成本最小化、可拆分物品的背包类简化问题

【解题思路】

【算法标签】

普及- #贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, m, ans=0;
struct milk {  // 定义农民结构体
    int p, m;  // 单价和牛奶量
}a[5005];  // 按照题目要求定义结构体数组
bool cmp (milk x, milk y)  // 定义排序函数
{
    return x.p<y.p;  // 按照单价从小到大排序
}
int main()
{
    cin >> n >> m;  // 输入n和m
    for (int i=1; i<=m; i++) {  // 依次输入m个农民牛奶的单价和能卖出的牛奶量
        cin >> a[i].p >> a[i].m;
    }
    sort(a+1, a+m+1, cmp);  // 按照牛奶的单价从小到大进行排序
    for (int i=1; i<=m; i++) {  // 依次遍历m个农民
        if (n>=a[i].m) {  // 如果牛奶的总量还大于农民可提供的量
            ans += a[i].p * a[i].m;  // 费用就等于这个农民提供的单价乘上可提供的牛奶量
            n -= a[i].m;  // 总量减去已经提供的量
        } else {  // 否则说明无法购买这个农民所有的牛奶量
            ans += a[i].p * n;  // 就用剩余的牛奶量乘上这个农民提供的单价
            break;  // 退出循环,不再遍历
        }
    }
    cout << ans << endl;  // 打印费用
    return 0;
}

【运行结果】

100 5
5 20
9 40
3 10
8 80
6 30
630
posted @ 2026-08-03 08:46  团爸讲算法  阅读(27)  评论(0)    收藏  举报