1. 题目背景
这段代码解决的是经典的部分背包问题(Fractional Knapsack):给定x件物品,每件物品有质量m和价值v,背包容量为y,要求在不超过背包容量的前提下,使装入背包的物品总价值最大。
与 0-1 背包不同,这里的物品可以分割(比如大米、面粉、金砂),所以我们可以按比例取走一部分,这就让贪心策略成为可能。
2. 核心思路:贪心策略
贪心的关键在于性价比——单位质量的价值(v / m)。我们总是优先选择「单位价值最高」的物品,能全装就全装,装不下就按剩余容量取一部分,直到背包装满。
这个策略之所以正确,是因为物品可分割:只要每次都拿当前性价比最高的,最终一定能得到全局最优解。
3. 代码逐行讲解
3.1 结构体与全局数组
structValue{floatm;// 物品质量floatv;// 物品价值};Value values[105];// 最多 105 件物品定义结构体Value保存每件物品的质量m和价值v,并用全局数组values存储所有物品。
3.2 排序比较函数
boolcmp(Value a,Value b){returna.v>b.v;// 按单位价值降序排列}这是sort的比较函数。注意:在main中已经提前把v变成了「单位价值」(v / m),所以这里直接按v降序排序即可,性价比高的物品排在前面。
3.3 主函数:输入与预处理
intx,y;cin>>x>>y;// x 件物品,背包容量 yfor(inti=0;i<x;i++){cin>>values[i].m;// 输入质量cin>>values[i].v;// 输入价值}for(inti=0;i<x;i++){values[i].v/=values[i].m;// 计算单位价值 v/m}先读入物品数量和背包容量,再读入每件物品的质量与价值。随后把每件物品的v原地更新为单位价值(v / m),为排序做准备。
3.4 排序
sort(values,values+x,cmp);按单位价值从高到低排序,性价比最高的物品排在最前面。
3.5 贪心装入
floatsum=0;for(inti=0;i<x;i++){if(values[i].m<=y){// 当前物品能整件装下sum+=values[i].m*values[i].v;// 全装,价值 = 质量 × 单位价值y-=values[i].m;// 剩余容量减少}else{// 装不下整件,只能装一部分sum+=y*values[i].v;// 用剩余容量 y 乘以单位价值break;// 背包已满,结束}}printf("%.2f",sum);// 保留两位小数输出最大总价值这是核心循环:
- 若当前物品质量
m不超过剩余容量y,就整件装入,累加价值m × v(此时v是单位价值),并扣减剩余容量; - 若装不下整件,就按剩余容量取一部分,累加
y × v,然后break结束——因为背包已经满了,后面的物品即使性价比再高也装不进去了。
最后用printf("%.2f", sum)保留两位小数输出结果。
4. 复杂度分析
- 时间复杂度:排序为
O(x log x),贪心装入为O(x),整体O(x log x)。 - 空间复杂度:
O(x),用于存储物品数组。
5. 易错点提醒
- 单位价值要提前算好:排序前必须把
v更新为v / m,否则排序依据错误。 - 浮点精度:
m、v用float,计算v / m时注意类型,避免整数除法丢失小数。 break不能漏:当装不下整件、只能取一部分时,背包已满,必须跳出循环,否则会继续错误累加。- 输出格式:题目要求保留两位小数,用
printf("%.2f", sum)或cout << fixed << setprecision(2)。
6. 总结
这段代码是贪心算法处理部分背包问题的经典实现:先算性价比(单位价值),按性价比降序排序,再依次装入,装不下就取一部分。理解「物品可分割」这一前提,是理解整个贪心策略正确性的关键。