01背包问题

01背包是经典的动态规划的问题。做了两次才理解到一些毛皮,记下来方便以后复习。

WP上动态规划的的适用情况如下。

  1. 最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。
  2. 无后效性。即子问题的解一旦确定,就不再改变,不受在这之后、包含它的更大的问题的求解决策影响。
  3. 子问题重叠性质。子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。

01背包解题思路大致就是:

给定这些物品的重量(用数组[]weight表示)和价值(用数组[]value表示),背包的最大能装的重量为w,最多能装物品的数量为n。

设一个数组Max[i][j] 其实i是选择了前面的多少件物品,j就是当前背包的剩余容量。Max[i][j]的值就是当前最大价值

Max数组的大小其实可以确定,int Max[][] = new int[n+1][w+1],因为循环是从1开始的,防止角标越界。


递归出口是什么呢?

当递归到背包不能再装东西的情况,也就是Max[i][0] = 0;

当递归到背包只能装0件物品时,也就Max[0][j] = 0;


再推算出状态转移方程。一共就两个状态的:

容量放不下,直接不拿:Max[i][j] = Max[i-1][j],也就是不拿当前的i物品,价值和当前背包容量不变,和拿了第i-1个物品的价值重量一样。

容量放的下。拿的话就分两种情况。要比较谁大谁小。

一个是不拿:Max[i][j] = Max[i-1][j]。

一个拿:Max[i][j] = Max[i-1][j-weight[i-1]] + value[i-1].请注意这里的weight[i-1]和value[i-1]

import java.util.Scanner;
class Knapsack01{		
	public static void main(String []args){
		Scanner sc = new Scanner(System.in);
		int w = sc.nextInt(); //背包的最大能装多少重量
		int n = sc.nextInt(); //背包最多能装物品的数量   
		
		int []weigth = {2,2,6,5,4};//物品的重量  
		int []value = {6,3,5,4,6};// 对应物品价值  
		
		DP(w,n,weigth,value);
	}
	
	public static void DP(int w,int n,int []weigth,int []value){
		int [][]Max = new int [n+1][w+1];
		
		for(int i=0;i<=n;i++)
			Max[i][0] = 0;//背包不能再装东西的情况,递归出口
		for(int j=0;j<=w;j++)
			Max[0][j] = 0;//已经到了前0件物品的选择,也是递归出口
		
		//i为前n件物品,j为背包的当前容量
		for(int i=1;i<=n;i++){
			for(int j=1;j<=w;j++){
				if(weigth[i-1] <= j)
					Max[i][j] = Math.max(Max[i-1][j],Max[i-1][j-weigth[i-1]]+value[i-1]);
					//weigth数组和value数组是从0开始的,而Max[i][j]数组中,j是从1开始的
                                        //要搞清楚当前的物品是什么。
					else
					Max[i][j] = Max[i-1][j];	
			}
		}
		
		for(int i=0;i<Max.length;i++){
			for(int j=0;j<Max[i].length;j++){
				System.out.print(Max[i][j]+"     ");
			}
			System.out.println("");
		}
			
	} 
}

如果觉得weight[i-1]和value[i-1]看不不舒服,可以让weight数组和value从角标1开始赋值,然后再修改状态转移方程中的角标。

以上。


参考:https://www.cnblogs.com/lfeng1205/p/5981198.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值