如:背包的容量为W,现有n件物品,他们的重量分别为:w1,w2,w3……wn,价值分别为v1,v2,v3……vn,问如何存放物品,使背包内物品总价值达到最大,这就是01背包问题。
这里采用动态规划来解决。
这里用c(i,j)来表示当背包容量为j时,前i件物品的最大总价值
我们可以这样来想:每一件物品i都有两种可能,放与不放
1、放,可以先求出前i-1件物品在背包容量为j-wi时的最优解,再加上i物品的价值,就是当第i件物品放入背包中的最优解,
即:c(i,j)=c(i-1,j-wi)+vi;
2、不放,这个就简单了,直接c(i,j)=(i-1,j)。
最大值就取这两种情况里的最大值
当然当没有物品时或者背包容量为0时,即i=j=0时,c(i,j)=0
由此分析可得递推式:
例:有5件物品a,b,c,d,e,重量分别为3,5,6,4,2,价值分别为6,3,5,4,3,背包容量为10,下面这张表就是最大值形成过程
最大值找到了,接下来就是找是那几件物品被放入背包中了,我们可以从j=10,i=5时倒着找,将c(i,j)与c(i-1,j)比较,若不同,则说明第i件物品被放入,即这里的13不同11,说明e物品被放入,接下来就只看e被放入的情况,最大值就与c(i-1,j-wi),即表中深红色那个10有关,此时i=4,j=8,将将c(i,j)与c(i-1,j)比较,10不同9,说明d被放入……可以发现a,d,e这三件物品被放入。
代码如下:
java实现
输出结果01表示物品不放入与放入
import java.util.Scanner;
public class DP {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("箱子容量:");
int W=input.nextInt();//箱子容量
System.out.println("物品数量:");
int n=input.nextInt();//物品数量
int []w=new int[n+1];//物品重量
int []v=new int[n+1];//物品价值
int [][]wv=new int[n+1][W+1];//最大价值
int []a=new int[n+1];//存放 01,判断物品要不要放入
System.out.println("每一件物品的重量:");
for(int i=1;i<=n;i++){
w[i]=input.nextInt();
}
System.out.println("每一件物品的价值:");
for(int i=1;i<=n;i++){
v[i]=input.nextInt();
}
for(int i=1;i<=W;i++){
for(int j=1;j<=n;j++){
if(i>=w[j]){
wv[j][i]=Math.max(wv[j-1][i-w[j]]+v[j], wv[j-1][i]);
}
else{
wv[j][i]=wv[j-1][i];
}
}
}
int c=W;
for(int i=n;i>0;i--){
if(wv[i][c]==wv[i-1][c]){
a[i]=0;
}
else{
a[i]=1;
c=c-w[i];
}
}
System.out.println("取得最大价值:"+wv[n][W]);
for(int i=1;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
}
C实现
#include<stdio.h>
#include<malloc.h>
int max(int a,int b);
void backPack(int W,int w[],int v[],int n);
int main(){
printf("箱子容量:");
int W;//箱子容量
scanf("%d",&W);
printf("物品数量:");
int n;//物品数量
scanf("%d",&n);
int w[n];//物品重量
int v[n];//物品价值
printf("每一件物品的重量:");
for(int i=0;i<n;i++){
scanf("%d",&w[i]);
}
printf("每一件物品的价值:");
for(int i=0;i<n;i++){
scanf("%d",&v[i]);
}
backPack(W,w,v,n);
return 0;
}
void backPack(int W,int w[],int v[],int n){
int wv[n][W+1];//最大价值
int a[n];//存放 01,判断物品要不要放入
for(int j=1;j<W+1;j++) {
for(int i=0;i<n;i++) {
if(i-1>=0) {
wv[i][j] = max(wv[i-1][j] , j-w[i]>=0 ? (wv[i-1][j-w[i]] + v[i]) : wv[i-1][j]);
} else {
wv[i][j] = j-w[i]>=0?v[i] : 0;
}
}
}
int c=W;
for(int i=n-1;i>=0;i--){
if(wv[i][c]==wv[i-1][c]){
a[i]=0;
}
else{
a[i]=1;
c=c-w[i];
}
}
printf("取得最大价值:%d\n",wv[n-1][W]);
for(int i=0;i<n;i++){
printf("%d\t",a[i]);
}
}
int max(int a,int b){
if(a>=b)
return a;
return b;
}