笔记:洛谷 P3985 不开心的金明
算法笔记: [背包问题] 洛谷P3985 不开心的金明
题目详情
原题链接:洛谷P3985 不开心的金明
不开心的金明
Description
金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说了不算(有很大的限制),而且不超过W元钱。”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的W元。于是,他把每件物品规定了一个重要度整数
妈妈看到购物单后进行了审查,要求购物单上所有的物品价格的极差(最贵的减去最便宜的)不超过3(当然金明至今不知道为什么会这样)。他希望在不超过W元(可以等于W元)的前提下,使购买的重要度总和
请你帮助金明设计一个满足要求的购物单,你只需要告诉我们重要度的最大的和。
Input Format
输入的第1行,为两个正整数,用一个空格隔开:
n W (其中W表示总钱数,n为希望购买物品的个数。)
从第2行到第n+1行,第j行给出了编号为j-1的物品的基本数据,每行有2个非负整数v p (其中v表示该物品的价格,p表示该物品的重要度)
Output Format
输出只有一个正整数,为不超过总钱数的物品的重要度的总和的最大值
I/O Sample #1
Sample Input #1
5 10 2 800 5 400 5 300 3 400 2 200
Sample Output #1
1600
Hint
对所有的
疑惑的来源
起初我只是把它当作简单的背包问题,但是观察了数据规模后发现商品的单价dp[w]
,所以解法势必根据数据规模进行优化。而我发现有以下两种解法:
解法一 优化价格 使用三维数组dp[i][w][k]
(恰当的解法)
优化价格
找出商品中的最小价格
使用三维数组dp[i][w][k]
首先描述一下数组dp
各个维度的意义
i
表示遍历到第i
个商品时的状态
对于遍历到的每一个物品,金明都可以选择买或者不买w
表示当前的预算大小
相当于子背包或者子问题,最终问题的答案(即在金明妈妈给出的预算内怎么买商品)由较小预算的状态推演过来.k
表示购买的商品数量
例如当dp[5][10][4]
表示金明希望购买5件商品,但妈妈只给出10块钱预算的情况下,买4件商品的最优方案.(可能不存在)
如何遍历三维数组dp[][][]
直接上代码
// 从清单中逐一挑选商品 for (int i = 1; i <= n; i ++) /* 由于我们不知道优化后的总预算是多少, 所以这里暂且用优化后的商品总价代替. */ for (int j = sumv; j >= p[i]; j --) // 购买数量亦是状态里的一部分 for (int k = n; k >= 1; k --) // 确保不超出预算则更新状态 if ((long long) minv * k + j <= w) // 在此处更新dp的状态 dp[i][j][k] = max(dp[i - 1][j][k], dp[i][j - p[i]][k - 1] + c[i]); else // 没得选择 dp[i][j][k] = dp[i - 1][j][k];
然后就是状态转移方程
dp[i][j][k] = max(dp[i - 1][j][k], dp[i][j - p[i]][k - 1] + c[i]);
状态方程的具体意思是,当前dp[i][j][k]
的取值由买和不买决定:
如果买,则取值为上一个商品i - 1
的各种状态中,花费j - p[i]
,共购买了k
件商品的状态,由该状态下的重要度加上当前商品的重要度,即dp[i][j - p[i]][k - 1] + c[i]
为取值;
如果不买或者超出总预算(else
中的语句),则直接继承上一个商品i - 1
同维度下的状态即可.
总之,可买可不买取其大者,买不了则继承之前的状态.
其实还有优化的余地:滚动二维数组
以下是优化后的代码
for (int j = sumv; j >= p[i]; j --) for (int k = n; k >= 1; k --) if ((long long) minv * k + j <= w) dp[j][k] = max(dp[j][k], dp[j - p[i]][k - 1] + c[i]);
优化后尤其要注意状态维度的遍历顺序,因为维度i
被优化掉了,那么更新状态时,数组dp[][]
中同时存在着新旧状态,新状态需要由旧状态中的小者更新而来,所以我们倒序遍历各个状态维度,即可保证还未被转移的旧小状态,不会被新小状态替代.
解法二 01背包 + 贪心(不太恰当的解法)
根据数据规模的大小,分别采用两种方法,当规模较小时,按照普通的01背包问题解决;当规模较大时,使用贪心算法,题解中给出的理由是:当商品的最小价格
对于一维数组dp
的规模极限,由于商品价格极差不大于3,所以可能有
勘误
勘误推理参考:君义_noip的证明
实际上
当
例如以下数据
800 3 400 3 400 4 401 5
此时贪心给出的答案为5, 而正确答案为7