背包问题wiki
f[i][v]=max{ f[i-1][v], f[i-1][v-w[i]]+v[i] }。
可以压缩空间,f[v]=max{f[v],f[v-w[i]]+v[i]}
可以想象这样一个场景——小偷在屋子里偷东西,他带着一只背包。屋子里物品数量有限——每件物品都具有一定的重量和价值——珠宝重量轻但价值高,桌 子重但价值低。最重要的是小偷背包容量有限。很明显,他不能把桌子分成两份或者带走珠宝的3/4。对于一件物品他只能选择带走或者不带走。
示例:
Knapsack Max weight : W = 10 (units)
Total items : N = 4
Values of items : v[] = {10, 40, 30, 50}
Weight of items : w[] = {5, 4, 6, 3}
从示例数据大致估算一下,最大重量为10时背包能容纳的物品最大价值为50+40=90,重量为7。
3
解决方法:
最佳的解决方法是使用动态规划——先得到该问题的局部解然后扩展到全局问题解。
构建物品X在不同重量时的价值数组V(Value数组):
V[N][W] = 4 rows * 10 columns
该矩阵中的每个值的求解都代表一个更小的背包问题。
初始情况一:对于第0列,它的含义是背包的容量为0。此时物品的价值呢?没有。因此,第一列都填入0。
初始情况二:对于第0行,它的含义是屋内没有物品。那么没有任何物品的背包里的价值多少呢?还是没有!所有都是0。
步骤:
1、现在,开始填入数组每一行的值。第1行第1列代表什么含义呢?对于第一个物品,可以把重量为1的该物品放入背包吗?不行。第一个物品的重量是5。因此,填入0。实际上直到第5列(重量5)之前都应该填入0。
2、对于第1行的第5列(重量5),意味着将物品1放入背包。填入10(注意,这是Value数组):
3、继续,对于第6列,我们可以再放入重量为1(重量值-物品的重量)的物品吗。我们现在只考虑物品1。由于我们加入物品1之后就不能再加入额外的重量,可以很直观地看到其余的列都应该还是相同的值。
4、接着,有意思的事情就要出现了。在第3行第4列,此时重量为4。
需要作以下判断:
1.可以放入物品2吗——可以。物品2的重量为4。
2.不加入物品2的话当前已有物品的重量的Value值是否更大——查看相同重量时的前一行的值。不是。前一行的值为0,重量4时不能放入物品1。
3.在这个重量时可以放入两件物品使得价值最大吗?——不能。此时重量减去物品2的重量后为0。
为什么是前一行?
简单来说,重量为4的前一行的值本身就是个更小的背包问题解,它的含义是到该重量时背包内物品的最大价值(通过遍历物品得到)。
举个例子:
当前物品价值 = 40
当前物品重量 = 4
剩余重量 = 4-4 = 0
查看上面的行(物品1或者其余行的值)。剩余容量为0时,可以再容纳物品1吗?对于该给定的重量值上面的行还有任何值吗?
计算过程如下:
1) 计算不放入该物品时该重量的最大价值:
previous row, same weight = 0
=> V[item-1][weight]
2) 计算当前物品的价值 + 可以容纳的剩余重量的价值
Value of current item
+ value in previous row with weight 4 (total weight until now (4) - weight of the current item (4))
=> val[item-1] + V[item-1][weight-wt[item-1]]
找到二者之中的最大值40(0和40)。
3) 下一次最重要的位置为第2行第9列。意味着此时重量为9,放入两件物品。根据示例数据现在可以放入两件物品。我们作了以下判断:
1. The value of the current item = 40
2. The weight of the current item = 4
3. The weight that is left over = 9 - 4 = 5
4. Check the row above. At the remaining weight 5, are we able to
计算如下:
不加入该物品时该重量的最大价值:
previous row, same weight = 10
计算当前物品的价值+可以容纳的剩余重量的价值
Value of current item (40)
+ value in previous row with weight 5 (total weight until now (9) - weight of the current item (4))
= 10
10vs50 = 50。
解决了所有的子问题之后,返回V[N][W]的值——4件物品重量为10时:
复杂度
解法的复杂度非常直观。在N次循环中有W次循环 => O(NW)
实现
var
i,j,v,n:longint;
f,c,w:array[0..100] of longint;
functionmax(a,b:longint):longint;
begin
ifa>bthenexit(a)elseexit(b);
end;
begin
read(n,v);
fillchar(f,sizeof(f),0);
fori:=1tondo
read(c[i],w[i]);
fori:=1tondo
forj:=c[i]tovdo
f[j]:=max(f[j],f[j-c[i]]+w[i]);
writeln(f[v]);
end.
Java代码实现:
class Knapsack {
public static void main(String[] args) throws Exception {
int val[] = {10, 40, 30, 50};
int wt[] = {5, 4, 6, 3};
int W = 10;
System.out.println(knapsack(val, wt, W));
}
public static int knapsack(int val[], int wt[], int W) {
//Get the total number of items.
//Could be wt.length or val.length. Doesn't matter
int N = wt.length;
//Create a matrix.
//Items are in rows and weight at in columns +1 on each side
int[][] V = new int[N + 1][W + 1];
//What if the knapsack's capacity is 0 - Set
//all columns at row 0 to be 0
for (int col = 0; col <= W; col++) {
V[0]<div class="column col-1-2"><p></p></div> = 0;
}
//What if there are no items at home.
//Fill the first row with 0
for (int row = 0; row <= N; row++) {
V[row][0] = 0;
}
for (int item=1;item<=N;item++){
//Let's fill the values row by row
for (int weight=1;weight<=W;weight++){
//Is the current items weight less
//than or equal to running weight
if (wt[item-1]<=weight){
//Given a weight, check if the value of the current
//item + value of the item that we could afford
//with the remaining weight is greater than the value
//without the current item itself
V[item][weight]=Math.max (val[item-1]+V[item-1][weight-wt[item-1]], V[item-1][weight]);
}
else {
//If the current item's weight is more than the
//running weight, just carry forward the value
//without the current item
V[item][weight]=V[item-1][weight];
}
}
}
//Printing the matrix
for (int[] rows : V) {
for (int col : rows) {
System.out.format("%5d", col);
}
System.out.println();
}
return V[N][W];
}
}
程序
程序一:
var
i,j,v,n:longint;
f,c,w:array[0..100] of longint;
function max(a,b:longint):longint;
begin
if a>b then exit(a) else exit(b);
end;
begin
read(n,v);
fillchar(f,sizeof(f),0);
for i:=1 to n do
read(c[i],w[i]);
for i:=1 to n do
for j:=v downto c[i] do
f[j]:=max(f[j],f[j-c[i]]+w[i]);
writeln(f[v]);
end.
程序二(顺推法):
var m,n,x,i:integer;
c,w:array[1..30] of integer;
f:array[0..30,0..300] of integer;
function max(x,y:integer):integer;
begin
if x>y then max:=x else max:=y;
end;
begin
readln(n,m);
for i:=1 to n do
readln(c[i],w[i]);
for i:=1 to n do
for x:=1 to m do
if x>=c[i] then f[i,x]:=max(f[i-1,x-c[i]]+w[i],f[i-1,x])
else f[i,x]:=f[i-1,x];
writeln(f[n,m]);
end.
C的实现
#include<stdio.h>
#include<malloc.h>
typedefstruct
{
intobject;
intweight;
intvalue;
}KnapSack;
KnapSack*knapsack;//背包数组,用malloc或new动态创建
intnum;//物体的个数
intcontainer;//背包的最大容量
int**array=NULL;//用来存放子问题的结果
//动态创建背包
voidCreate_KnapSack()
{
charc;
printf("inputthenumberofobjects\n");
scanf("%d",&num);
knapsack=newKnapSack[num+1];
printf("inputweightandvalueof%dobjects,like1:410\n",num);
for(inti=1;i<=num;i++)
{
scanf("%d%c%d%c%d",&knapsack[i].object,&c,&knapsack[i].weight,&c,&knapsack[i].value);
getchar();//为了获取空格或其他输入,声明下scanf挺恶心
}
intk=knapsack[num].value;
printf("%d",k);
printf("inputthevolumeoftheknapsack:\n");
scanf("%d",&container);
}
//确定最优子问题
voidResolve_KnapSack()
{
intk=knapsack[num].value;
printf("%d",k);
//创建动态二维数组m[num][container]
array=(int**)malloc((num+1)*sizeof(int*));
for(inti=0;i<=num;i++)
array[i]=(int*)malloc((container+1)*sizeof(int));
//
for(intj=0;j<=container;j++)
array[num][j]=(j>=knapsack[num].weight)?knapsack[num].value:0;
//子问题的最优结果
for(intm=num-1;m>0;m--)
for(intn=0;n<=container;n++)
if(n>knapsack[m].weight&&array[m+1][n]<=array[m+1][n-knapsack[m].weight]+knapsack[m].value)
array[m][n]=array[m+1][n-knapsack[m].weight]+knapsack[m].value;
//else包括两种情况,共同点是该物体没有被使用
else
array[m][n]=array[m+1][n];
}
//往回找,确定某个物体i是否被使用
bool*Trace_back()
{
intc=container;
bool*used;
used=(bool*)malloc(sizeof(bool)*(num+1));
for(inti=1;i<num;i++)
if(array[i][c]==array[i+1][c])
used[i]=0;
else
{
used[i]=1;
c-=knapsack[i].weight;
}
used[num]=(c>=knapsack[num].weight)?1:0;
returnused;
}
//用来输出被使用的物体及其相关值
voidPrint_KnapSack(bool*used)
{
printf("theobjectsusedasfollows:\n");
for(inti=1;i<=num;i++)
if(used[i])
printf("%d:%d%d\n",knapsack[i].object,knapsack[i].weight,knapsack[i].value);
}
voidmain()
{
bool*used;
Create_KnapSack();
Resolve_KnapSack();
used=Trace_back();
Print_KnapSack(used);
}