辨析:分数背包=贪心
可分割⇒贪心 vs 整取⇒DP
本课摘要
辨析:分数背包=贪心课程回答“为什么可分割物品属于贪心而不是 01 背包”。内容以可分割⇒贪心 vs 整取⇒DP为主线,配合逐步推导、可编辑演示、例题与练习形成可复查的学习闭环。
- 判断辨析:分数背包=贪心的适用条件与状态边界
- 围绕“可分割⇒贪心 vs 整取⇒DP”推导转移与计算顺序
- 用演示、复杂度分析和配套题目校验实现
本课目录 · 0 节
正在整理目录…
先分清:这次能不能「取一部分」
前面几种背包,物品都是整件取舍,一件要么整个拿走、要么留下,没有「拿半件」这回事。可现实里有另一类物品: 金粉、牛奶、汽油、矿砂……它们可以只取一部分,装满剩余空间的一小段也行。这类问题叫分数背包(也叫部分背包)。
差别看着小,分量却很重。回想 01 背包的开头:那里我们试着用贪心(按性价比 从高到低装),结果输给了 DP,因为整件取舍时,塞不下的那件只能整个放弃,贪心会在「差一点点」的地方卡住。 但只要物品可以切开,这个「差一点点」就消失了:装不下整件?那就切下正好填满的一段。于是,贪心重新变成最优,而且不再需要 DP。这一页专门点破这条分界。
可切分时,贪心为什么就是最优
策略只有一句话:按单位价值 从高到低装,能整件装就整件装,直到最后装不下整件的那一件,按剩余容量的比例切开,把背包填满为止。
为什么这样一定最优?关键在「可切分」赋予的自由:背包最终一定会被恰好填满(除非所有物品都装进去还有空)。既然容量必被占满,那把每一单位容量都留给单位价值最高的物品,总价值自然最大。 严格一点说,用交换论证:取任一「最优」方案,若其中某一单位空间给了 较低的物品,而更高性价比的物品还没装满,那就把这一单位切下来,换成高性价比那种同样一单位。空间占用不变(可切分保证换得进),而总价值只增不减(换进来的每单位价值更高)。既然任何「低 抢占了本可给高 的空间」的方案都能这样被改良,最优方案里就不可能存在这种错配:必被填满的每一单位,都归当前剩余里 最高的物品,这恰好就是「按 降序填」的贪心。
用 01 背包开头的同一组数据体会一遍:物品 ,容量 。
对照一下:同样这组数据若只能整取(01 背包),最优是 ,因为 要么整件塞进去、要么彻底放弃,没法只填那 3 格的缝隙。 可切分把这 的差额补了回来。
本质 · 为什么这里贪心够用、轮不到 DP
分数背包的最优子结构被「可切分」抹平了:容量必被填满,每一单位空间独立地归给单位价值最高者即可,当前最优不再牵扯后面还剩多少整数空间。于是一次排序 + 一趟扫描()就得最优,不需要背包 DP 那张表。DP 是用来对付「整件取舍」那种牵一发动全身的耦合的,这里没有那种耦合。
跟着装一遍
把贪心用那组数据(物品 ,容量 )从头装一遍,每一步只做一个动作:
改改看:贪心 vs 整取,两个数一起跳
下面把两条路并排算给你看:左边是可分割 → 贪心(按 降序填、最后一件切开),右边是若整取 → 01-DP 最优(自写一个小背包)。 改物品的 或容量 ,盯住那条容量条:整件段是实心、被切开的尾段是斜纹。多数情况下贪心的数更大(切开填满了整取留下的缝隙);当数据恰好整取就能填满时,两者持平。贪心永远 整取,绝不会更差。
一句话分界:可分割⇒贪心,不可分割⇒背包 DP
把这一部分的整条脉络收束成一个判别动作,拿到一道「装东西求最值」的题,先问一句:物品能不能取一部分?
能切分(金粉、牛奶、汽油、按重量卖的散货)→ 按 降序贪心,最后一件切开填满,,用不到 DP。
整件取舍(一台机器、一本书、一件装备,只能整个拿或不拿)→ 贪心会在「差一点点」处失手,必须回到背包 DP:01 / 完全 / 多重…… 用一张表把指数级组合压成多项式。
整取时贪心的经典反例(回扣 01 背包)
就是 01 背包开头那一幕:物品 、容量 8,按 贪心先装 再装 ,剩 3 格塞不下 只得 7;最优却是 。整取时贪心输 2,因为那 3 格的缝隙没法用「半件 」去填。可一旦允许切分,这半件就能塞进去,反例当场消失,贪心反超到 10.75。能不能切开,就是贪心与 DP 的分水岭。
例题
#include <iostream>
#include <algorithm>
using namespace std;
struct Farmer // 一个奶农:单价 p、存量 a
{
int p, a;
};
Farmer g[5005];
bool cmp(const Farmer &x, const Farmer &y)
{
return x.p < y.p; // ★按单价升序:先买最便宜的
}
int main()
{
int n, m; // n 需求量,m 奶农数
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> g[i].p >> g[i].a;
sort(g + 1, g + m + 1, cmp); // 贪心的核心:排序
long long cost = 0; // 总花费
for (int i = 1; i <= m && n > 0; i++) // 需求没凑够就继续买
{
int buy = min(n, g[i].a); // ★可只买一部分:这家最多买 min(还差多少, 存量)
cost += (long long)buy * g[i].p;
n -= buy;
}
cout << cost << endl;
return 0;
}练习
这是一节辨析课,配套的洛谷原生题只有 P1208 一道,就不用非原生题凑数了。真正要带走的是这条判别直觉:
遇到「可取一部分 / 按重量按量买」,先想贪心(按 或单价排序);遇到「整件取舍、只能整个拿或不拿」,再回背包 DP。前八节的背包 DP 是为后一种情形准备的重武器;这一节告诉你,不是所有「装背包」都要动用它。

