01背包问题

原创文章,转载请注明!


背包问题

  1. 01背包: 有N件物品和一个重量为M的背包。(每种物品均只有一件)第i件物品的重量是w[i],价值是p[i]。求解将哪些物品装入背包可使价值总和最大。
  2. 完全背包: 有N种物品和一个重量为M的背包,每种物品都有无限件可用。第i种物品的重量是w[i],价值是p[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包重量,且价值总和最大。
  3. 多重背包: 有N种物品和一个重量为M的背包。第i种物品最多有n[i]件可用,每件重量是w[i],价值是p[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包重量,且价值总和最大。

这儿主要看问题1:


假设背包重量为total,物品重量w[0...len],物品价值v[0...len],c[i][j]表示前i个物品下在假设背包重为j时能取得的最大价值和。

递归公式:

c[i][j]=

0  i=0 or j=0    //前0个物品,或者说背包重量为0

max{c[i - 1][j], c[i-1][j-w[i]]+v[i]}  i > 0 and j - w[i] >= 0 //精华:考虑选择i物品或者不选两种情况

c[i-1][j]  i>0 and j-w[i]<0  //i物品重量大于背包重量,显然就不选i


优化:c的大小为(len+1)*(total+1),而实际上若设物品中最轻的为w[0],c[*][j]  j<w[0]全部为0,可以进行线性变换,若w[0]为最小,则将进行映射w[0]-->1, <w[0]-->0,total-->total-w[0]+1,这样c的大小为(len+1)*(total-w[0]+2),在很多情况下节省了大量空间

实际代码中假设w[0]为最小重量,实际也可以在函数中搜索出这个值。

#include <iostream>
#include <stdlib.h>
using namespace std;

int bag01(int weight[], int value[], int len, int total)
{
	int *c1 = new int [(len + 1) * (total - weight[0] + 2)];
	int **c = new int* [len + 1];
	for (int i = 0; i < len + 1; i++){
		c[i] = c1 + i * (total - weight[0] + 2);
	}
	int i, j;
	int max_temp;
	for (i = 0; i < len + 1; i++)
		c[i][0] = 0;
	for (i = 0; i < total - weight[0] + 2; i ++)
		c[0][i] = 0;
	for (i = 1; i < len + 1; i ++){
		for (j = weight[0]; j <= total; j++){
			if(j - weight[0] + 1 >= weight[i-1]){
				if(c[i - 1][j - weight[0] + 1] > c[i - 1][j - weight[0] + 1 - weight[i - 1]] + value[i - 1]){
					c[i][j - weight[0] + 1] = c[i - 1][j - weight[0] + 1];
				}
				else {
					c[i][j - weight[0] + 1] = c[i - 1][j - weight[0] + 1 - weight[i - 1]] + value[i - 1];
				}
			}
			else {
				c[i][j - weight[0] + 1] = c[i - 1][j - weight[0] + 1];
			}
		}
	}
	for (i = 0; i < len + 1; i++){
		for(j =0; j < total - weight[0] + 2; j++)
			cout<<c[i][j]<<" ";
		cout<<endl;
	}
	cout<<endl;
	max_temp =  c[len][total - weight[0] + 1];
	delete[] c;
	return max_temp;
}

int main()
{
	int weight[] = {1, 2, 3};
	int value[] = {6, 10, 12};
	int len = sizeof(weight)/ sizeof(int);
	int total = 5;
	cout<<"max value is"<<bag01(weight,value,len,total)<<endl;
	system("pause");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值