原创文章,转载请注明!
背包问题
- 01背包: 有N件物品和一个重量为M的背包。(每种物品均只有一件)第i件物品的重量是w[i],价值是p[i]。求解将哪些物品装入背包可使价值总和最大。
- 完全背包: 有N种物品和一个重量为M的背包,每种物品都有无限件可用。第i种物品的重量是w[i],价值是p[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包重量,且价值总和最大。
- 多重背包: 有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");
}