目录
题目描述
有一个箱子容量为 V,同时有n 个物品,每个物品有一个体积。
现在从 n个物品中,任取若干个装入箱内(也可以不取),使箱子的剩余空间最小。输出这个最小值。
输入格式
第一行共一个整数 V,表示箱子容量。
第二行共一个整数 n,表示物品总数。
接下来 n 行,每行有一个正整数,表示第 i 个物品的体积。
输出格式
-
共一行一个整数,表示箱子最小剩余空间。
输入输出样例
输入
10
4
20
5
1
9
输出
0
说明/提示
对于100% 数据,满足 0<n≤30,1 ≤V≤20000。
解题思路
这题可以用动态规划(DP)--一维01背包的思想去做(如果不清楚01背包,可翻到下方,下方有详细的解释)
-
将这里的物品体积看成是01背包问题中的物品价值
-
将01背包最后的输出换成“箱子容积 - 当前物品可放最大体积(物品当前最高价值) ”就是箱子剩余体积啦!
代码
#include<iostream>
using namespace std;
int V[1000]; //相当于箱子
int main(){
int v,n;// v:箱子的容量 n: 物品数量
cin>>v>>n;
int N[n]; //用来存入每个物品的体积
for(int i = =1; i <= n ; i++){
int Vn = 0;
cin>>Vn;
N[i] = Vn; //录入每个物品的体积
}
for(int i = 1 ; i <= n ; i++){
for(int j = v ; j >= N[i] ; j--){
V[j] = max(V[j],V[j - N[i]] + N[i]);
}
}
cout<<v - V[v]<<endl;
return 0;
}
01背包
(每个物体只能拿一次,要求在一定的空间内,拿物体使得到的价值最大)
例如:
背包容积 | 待放入背包中的物品体积 | 物品价值 |
---|---|---|
10 | 20 | 5 |
5 | 6 | |
1 | 3 | |
9 | 6 |
一维01背包算法重点在于:
-
最右下角为最大数字的是当前最大价值(当前最大价值为9)。
-
如果当前最大价值的上一层也是同等数字代表当前物品没拿或只拿了当前物品。(当前最大价值是9,但是9上方也是9代表这个物品可以选择不拿或者只拿当前物品),如若数值不一致,我们也可以由此判断出到底是拿当前物品价值最高还是拿其他物品价值最高。(如下图)
这里我们将最后体积为9的物品换为体积为10,价值为1的物品。我们会发现我们最右下角的数字为1,但是上方为9,显然是价值为9比较划算!我们也可以由此比较出到底选哪一组组合价值更高。我们也可以来验证一下第一条重点。当前最右下角为最大数字的就是9。(算法如下)
wei[i]:当前物品体积
val[i]:当前物品价格
dp[j-wei[i]]+val[i]:装了i物品后背包中所有物品价值
dp[j]:未装i物品的背包中所装物品价值
解释:当前V[10]为9,V[10 - 9] + 6 = V[1] + 6 = 3+6 = 9
状态转移方程: dp[j]=max(dp[j],dp[j-wei[i]]+val[i])
注意:从体积为1的物品来算,dp[1] ~ dp[4] = 3 dp[5] = max(dp[5],dp[5-1]+3) = 6(这里注意:我们是从后往前推的,不是9是正常现象,但是不影响最后的结果)
dp[6] = max(dp[6],dp[6-1]+3) = 9 ,后面就一致了(如果数据多可能在中途还会出现几次,但是完全不影响后续的答案!)
- 关于算法,通常是从后往前来推;也就是从最右下角有数字(除0之外)开始从前推。
基本操作:
for(int i= 0;i< n;++i)
{
for(int j=v;j>=wei[i];--j)
{
dp[j]=max(dp[j],dp[j-wei[i]]+val[i]);
}
}
cout<<dp[v]<<endl;
这里的j = v(当前背包容积),可以倒推一下如果(j = 0;j < wei[i]; j++),我们从左上角开始往下推,显然是不对的。
我们从物品体积为20的开始推算(这里是遵循了数组从0开始的原则):
dp[0] = max(dp[0],dp[0 - wei[20]]+val[20]) (问题一:显然不对!dp[0 - wei[20]]+wei[20]为负值了,数组最小位数为0;接下来也没必要推导了)
那我们再将dp[j] = max(dp[j],dp[j - wei[i]] + val[i]) 换为 max(dp[j],dp[wei[i] - j ] + val[i])
dp[0] = max(dp[0],dp[20 - 0] + val[20]) = dp[20] + val[20](显然也不对!背包根本没有那么大!)
我们从体积为5开始推算:
(现在要推导的公式:dp[j] = max(dp[j],dp[wei[i] - j ] + val[i]))
dp[0] = max(dp[0],dp[5]+val[5]) = 6(问题二:这根本也不对!完全违背了表格!)
dp[0] ~ dp [5] = 6
接下来是体积为1:
dp[0] = max(dp[0],dp[1]+val[1]) = 3
dp[1] = max(dp[1],dp[0] + val[1]) = dp[0] + val[1] = 3+3 = 6(问题三:显然重复加了同一个物品的价值!)
dp[2] = max(dp[2],dp[-1] + val[1]) (Bug!数组最小位数为0!)