动态规划总体的思想与分治法类似,也是将带求解的问题分解成若干个子问题。但是再使用动态规划的时候由于处理子问题比较多,而且这些子问题的数据在后面的操作中会使用到,因此在动态规划使用的时候还需要建立一个“表”。这个“表”就是一个数组,用于记录在处理每一分支的时候的数据。总体的思路还是将带求解的问题分解成若干个子问题 ,最后将这些子问题合并求得要求解的问题。
0-1 背包问题,指的是一个重量为W的背包,有着n个物品,这些物品的重量分别为wi(i<n),并且这些物品都有价值,分别为vi。要求如何装这些物品能够使得包中的物品的总价值最大。
用动态规划来处理这个问题时,则是将这个问题分解成如若干小问题:当背包质量为w时,放入前n个物品是取得最大的价值是多少?因此这个问题就从最基本的问题开始求:背包的质量为0,放入前0个物品,总价值最大为0。背包质量和物品个数依次增加,每一个物品在选择放入背包中都是只有两种状态:放入、不放。
设v[i,w]表示重量为w的背包中装入前i个物品的最大价值。vi表示第i个物品的价值,wi表示第i个物品的重量。
那么在整个过程中,对于每一个情况存在着三种情况:
(1)当i=0或者w等于0时,v[i,w]=0。当i=0,表示没有物品放入背包中,价值当然为0,同理背包重量为0,没有物品放入背包中,价值为0.
(2)当wi>w时,v[i,w]=v[i-1,w]。这个表示当第i个物品的重量大于w时,即这个物品没法放入背包中的,那么此时的价值为放入前i-1个物品的价值,因此v[i,w]=v[i-1,w]。注意:每一个v[i,w]既可以表示第i个物品放入背包中,也可以表示第i个物品没有放入背包中,只不过两种情况下的v[i,w]的值不同而已。
(3)当wi<=w&&i>0,v[i,w]=max{ v[i-1,w-wi]+vi , v[i-1,w] }。这表示当第i个物品可以放入背包中,那么比较放入包中的价值和没有放入包中的价值。放入包时:价值为v[i-1,w-wi]+vi,因为此时w代表加上了wi的值,因此要求没有放入第i个物品的价值求的是v[i-1,w-wi];没有放入包时:价值为v[i-1,w],这时w不包含wi,因此不需要减去wi。
因此求解该问题就是一次求解子问题,最后求出最大的价值。代码如下:
public class dongtaiguihua01 {
public static void main(String []args)
{
int W=17; //背包的总重量
int []w= {0,3,4,7,8,9}; //物品的相应重量
int []v= {0,4,5,10,11,13}; //物品的相应价值
int [][]value=new int [6][18];
int []num= new int [5];
for(int i=0;i<6;i++)
{
for(int j=0;j<18;j++)
{
if(i==0||j==0)
{
value[i][j]=0; //第一种情况
}
else if(w[i]>j)
{
value[i][j]=value[i-1][j]; //第二种情况
}
else if(i>0&&w[i]<=j) //第三种情况
{
int num1,num2;
num1=value[i-1][j-w[i]]+v[i]; //放入后的价值
num2=value[i-1][j]; //没有放入的价值
if(num1>num2)
{
value[i][j]=num1;
}
else
{
value[i][j]=num2;
}
}
}
}
//已经求出最大的价值,并且已经知道最后一个元素是否放入包中
for(int i=4;i>=0;i--)
{
if(value[i+1][W]!=value[i][W])
{
num[i]=1;
W=W-w[i+1];
}
else {
num[i]=0;
}
}
System.out.println("放入包中的物品为:");
for(int i=0;i<5;i++)
{
if(num[i]==1)
{
System.out.println("质量为:"+w[i+1]+",价值为:"+v[i+1]);
}
}
System.out.println("最大的价值为:"+value[5][17]);
}
}