1. 题目背景与核心考点解析
"Mooncake (25)"是PAT甲级考试中的一道经典算法题,主要考察贪心算法在实际商业场景中的应用能力。题目背景设定在中秋节月饼销售场景,要求考生计算在给定库存和总售价的情况下如何实现最大利润。
这道题在PAT甲级考试中属于中等难度,但非常具有代表性。我在第一次刷题时就被它"坑"过——看似简单的贪心策略,实际编码时却有几个关键陷阱需要注意。下面我就结合自己的刷题经验,详细拆解这道题的解题思路和实现细节。
2. 问题建模与算法选择
2.1 题目需求分析
题目给出:
- N种月饼的库存量(万吨)
- 这些月饼的总售价(亿元)
- 市场需求量D(万吨)
要求计算最大可能收益。这本质上是一个典型的"分数背包问题"(Fractional Knapsack Problem),具有以下特征:
- 物品可以分割
- 目标是最优价值
- 约束条件是总重量
2.2 贪心算法适用性证明
选择贪心算法的依据:
- 最优子结构:整体最优解包含子问题最优解
- 贪心选择性质:局部最优能导致全局最优
- 本题中单位重量的价值(售价/库存)是天然的最优度量标准
注意:与0-1背包问题的区别在于,月饼可以按重量分割销售,这是能用贪心算法的关键前提。
3. 详细实现步骤
3.1 数据结构设计
建议使用结构体存储月饼信息:
cpp复制struct Mooncake {
double inventory; // 库存量(万吨)
double total_price; // 总售价(亿元)
double unit_price; // 单价(亿元/万吨)
};
3.2 关键算法流程
- 计算每种月饼的单价(总售价/库存量)
- 按单价从高到低排序
- 依次取单价高的月饼,直到满足需求量
实现代码框架:
cpp复制bool cmp(Mooncake a, Mooncake b) {
return a.unit_price > b.unit_price;
}
double maxProfit(vector<Mooncake>& cakes, double D) {
sort(cakes.begin(), cakes.end(), cmp);
double profit = 0.0;
for(auto& cake : cakes) {
if(D <= 0) break;
double sell = min(cake.inventory, D);
profit += sell * cake.unit_price;
D -= sell;
}
return profit;
}
4. 常见陷阱与调试技巧
4.1 浮点数精度问题
实测发现几个关键点:
- 必须使用double类型,float精度不够
- 比较浮点数时避免直接使用==
- 输出时按要求保留2位小数
4.2 边界条件测试
必须测试的特殊情况:
- 库存总量刚好等于需求量
- 库存总量小于需求量
- 单价相同的不同月饼
- 零库存的特殊情况
4.3 性能优化
虽然题目数据量不大,但好的习惯很重要:
- 使用vector而非数组
- 提前计算unit_price避免重复计算
- 使用引用传递参数
5. 完整AC代码示例
cpp复制#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Mooncake {
double inventory;
double total_price;
double unit_price;
};
bool cmp(Mooncake a, Mooncake b) {
return a.unit_price > b.unit_price;
}
int main() {
int N;
double D;
cin >> N >> D;
vector<Mooncake> cakes(N);
for(int i=0; i<N; ++i) {
cin >> cakes[i].inventory;
}
for(int i=0; i<N; ++i) {
cin >> cakes[i].total_price;
cakes[i].unit_price = cakes[i].total_price / cakes[i].inventory;
}
sort(cakes.begin(), cakes.end(), cmp);
double profit = 0.0;
for(auto& cake : cakes) {
if(D <= 0) break;
double sell = min(cake.inventory, D);
profit += sell * cake.unit_price;
D -= sell;
}
printf("%.2f", profit);
return 0;
}
6. 刷题经验分享
在PAT考场环境下,建议采取以下策略:
- 先花2分钟手算样例,验证思路
- 注意题目中的单位(亿元/万吨)
- 输出格式必须完全匹配
- 遇到WA时优先检查:
- 浮点数精度
- 边界条件
- 排序规则
这道题我前后做了3遍才完全掌握。最深的体会是:贪心算法看似简单,但必须严格证明其正确性。在实际编程竞赛中,建议先把常见贪心问题(区间调度、分数背包等)的证明过程牢记于心。
