动态规划——01背包问题

如:背包的容量为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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值