nyoj860--又见01背包(01背包的另一种形态)

分析:此题咋一看,以为就是普通的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;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值