分析:此题咋一看,以为就是普通的01背包,于是很开心的快速花了几分钟敲了一份01背包代码:
#include<bits/stdc++.h>
using namespace std;
#define N 10005
int dp[N];
int weight[N]; //记录物品重量
int value[N]; //记录物品价值
int n; //物品个数
int W; //背包容量
int main(){
while(cin >> n >> W){
memset(dp,0,sizeof(dp));
memset(weight,0,sizeof(weight));
memset(value,0,sizeof(value));
for(int i = 1;i <= n;++i){
cin >> weight[i] >> value[i];
}
for(int i = 1;i <= n;++i){
for(int w = W;v >= weight[i];--W)
dp[w] = max(dp[w],dp[v-weight[i]]+value[i]);
}
cout << dp[W] << endl;
}
return 0;
}
结果Runtime error,顿时意识到不对,一般报这个错,大多是由于数组越界了,再仔细一读题,发现变量的范围很有趣,wi和W范围很大,而vi的范围却很小,那么就不难知道问题出在哪儿了。
在一般的01背包问题中,是以wi作为数组下标的,然而现在wi如此之大,固然申请不了这么大的数组了。所以必须得换个思路,回想一般01背包中的核心代码是dp[w] = max(dp[w],dp[w-weight[i]]+value[i]); 这是基于“对于包里重量一样的情况下,包里的总价值越大越好”这个思想,所以这里用的是max函数。那么就会想,是否可以以价值作为数组下标,然后基于“对于包里总价值一样的情况下,包里的总重量越小越好",那么自然会用min函数啦,核心代码就为dp[v] = min(dp[v],dp[v-value[i]]+weight[i]);
然后对应一般的01背包问题的代码,还要知道核心双重for中的第二个for中的v的初值为多少,也就是v的最大值为多少,这里就不用思考太多,直接以所有物品的价值之后作为v的上界。
这样核心代码:双重for循环就写好了。
那么最终答案又该如何输出呢?首先要明确两点:
①此时dp的下标就是价值量,也就是说最后要输出的是dp的下标。
②此时dp的值就是重量,所以要遍历找出dp的值刚好小于W的那个下标。
另外,背包问题中,都是下标靠后的更加优化,所以应该从后往前遍历。
下面附上代码:
#include<bits/stdc++.h>
using namespace std;
#define N 10005
int dp[N]; //dp[v]表示在背包中物品价值为v时的最小重量
int weight[N]; //记录物品重量
int value[N]; //记录物品价值
int n; //物品个数
int W; //背包容量
int main(){
while(cin >> n >> W){
int sumvalue = 0; //记录所有物品的价值总和,即价值的最大值
memset(dp,0x3f,sizeof(dp)); ///因为是找最小值,所以要初始化为无穷大
memset(weight,0,sizeof(weight));
memset(value,0,sizeof(value));
dp[0] = 0;
for(int i = 1;i <= n;++i){
cin >> weight[i] >> value[i];
sumvalue += value[i];
}
for(int i = 1;i <= n;++i){
for(int v = sumvalue;v >= value[i];--v)
dp[v] = min(dp[v],dp[v-value[i]]+weight[i]);
}
int index = sumvalue;
while(dp[index--] > W);
cout << index+1 << endl;
}
return 0;
}