动态规划解决0-1背包问题(Java代码简单实现)

写在前面,本文章的代码以及一些图片和做法是来自于这个篇博客。看了许多博客,看了他的才看懂了一些,下面是我的一些理解,才疏学浅,请理解。

最近在看算法,看到了动态规划那里,都说这个很难的,但是我硬着头皮看了下来,然后觉得又是可以接受的。我觉得有时候先看算法思想的描述和一些概念性的术语总是会看得糊里糊涂的,先不要深琢算法的思想,也可以看一下,不懂也没事,然后再找这个算法能解决的一些具体的场景,应用动态规划的场景就多了去了,比如什么最长公共子序列问题,爬楼梯,矩阵连乘,还有比较复杂的股票问题,再就是我这篇博客说的0-1背包问题,其实0-1背包实际意义也就那样,比如一个最大承重量为10kg的背包,然后有几件商品,都有对应的重量和价值,我们要做的就是确保承重量不超过背包的前提下放进最大价值的商品,为什么交01背包呢,就是每个物体只有两种选择,放或不放,就是0或1。然后解决也可以用不同的算法,比如回溯等等,下面要讲的是动态规划的算法。

下面先来说一下动态规划里面的一些基本术语:

状态:当前的价值

状态转移:目前价值转移到下一状态,下一价值。

最优子结构性质:最优解需要由每个最优子问题的解来构成。

自底向上去获得最优解:子问题一步步合成到最终解,最终解为最优解,合成途中每一个解都为最优子解。

解决0-1背包的具体逻辑:动态规划是自底向上的,就好比如建房子,我们必须从地基做起,由底部慢慢往上推,直到最终的结果,上面的楼层都必须建立在比他低的楼层上。动态规划解决 0-1背包也是这么个意思,我们以背包承重量10kg为最大值,然后我们把背包的承重量从0一直变化到最大值,比如0,1,2,3…10,每个承重量能我们都计算他放入物体的最大价值,接着我们需要从0的承重量开始计算背包能放入物体的最大价值,使用递推的方法直到最终解,每个结果保存下来,避免重复计算,直到计算10kg承重量背包的最大价值。

下面是正文:

0-1 背包问题

假设一个只能装10重量的背包,然后还有几件物体,分别有重量和价值,我们要做的是在不超过背包限定的重量的前提下能装到价值最大。

解决动态规划问题首先要确定状态转移方程。确定每个状态,每个状态都是由前面的状态比较得出来的。

maxvalue [ i ] [ j ] =
max{maxvalue [i-1] [j] , value[i-1] + maxvalue [i-1] [j-weight[i-1]] }

maxvalue [ i ] [ j ]就是状态,等号后面的状态转移方程,
参数 i为放入前i个物体,j为背包的最大承重量。

具体的意思是:例如求前3个物体背包承重量为6的最大价值,即maxvalue [3] [ 6 ] 那么我们先求前两个物体背包承重量为6的最大价值,然后再求前三个物体放入背包时有两种情况:
第一:第三个物体重量小于背包的重量,放不下,那么就是不放下第三个物体,相当于求前三个物体的最大价值,第三个物体没放下去,那么也就是求前两个物体能放下背包承重量为6的最大价值。

第二,(1)能放下,那么我们优先为第三个物体腾出空间,先放第三个,然后再求在背包减去第三个物体重量之后能承载物体最大重量的值,在这个值的前提下,能放前两个物体的最大价值,就是maxvalue [2] [ 6-第三物体的重量 ],这个最大价值前面已经求到了的,直接拿来用就行了。
(2)能放下第三个物体还有一种情况,就是我还是不放第三个物体,拿着这个值跟(放第三个物体的价值加上放前两个物体最大价值maxvalue [2] [ 6-第三物体的重量 ])这个值比较,看看谁大。大的一个就确定为状态值。

因为有可能就是我能放前两个物体,然后还有一点空间但是放不下第三个物体了,这个价值有可能比我先放第三个物体的价值要大,因为我放了第三个物体之后有可能前两个物体就放不了了,然后前两个物体的价值总和有可能比第三个的大,所以我不如选第一种放下第三个物体的方案。

还有前面的放前两个物体的最大价值也是知道的。这里我之前有一个疑问是:减去第三个物体的重量之后,如果背包不能放东西了呢,那也是没关系的,因为不能放东西承重量就是0,那么前两个东西放下去的最大价值就是0了,maxvalue[2,0],我们在前面确定了maxvalue[2,i],固定前面的再循环后面的i,每个i的状态都会求出来,所以不用考虑这个。然后前两个的意思就是,在这两个里面选能在最大承重量的前提下放下物体的最大价值,就是如果放不下,那就不放,放下一个就一个的价值,两个就两个的价值。可以选0,1或2即都选。


下面是代码:

public class get01PackageAnswer {

public static int knaspace(int[] weight,int[] value,int maxweight) {
	int n=weight.length;
	int[][] maxvalue=new int[n+1][maxweight+1];
	
	for (int i = 0; i < maxweight+1; i++) {
		maxvalue[0][i]=0;
		System.out.print(maxvalue[0][i]);
	}
	
	for (int i = 0; i < n+1; i++) {
		maxvalue[i][0]=0;
		System.out.print(maxvalue[i][0]);
	}
	
	for (int i = 1; i <=n; i++) {
		for (int j = 1; j <=maxweight; j++) {
			maxvalue[i][j]=maxvalue[i-1][j];
			
			if (weight[i-1]<=j) {
				if (maxvalue[i-1][j-weight[i-1]]+value[i-1]>maxvalue[i-1][j]){
					maxvalue[i][j] = maxvalue[i-1][j-weight[i-1]]+value[i-1];
				}
			System.out.println(maxvalue[i][j]);
			}
		}
	}
		
return maxvalue[n][maxweight];
}

public static void main(String[] args) {
	int weight[]= {2,3,4,5};
	int value[]= {3,4,5,7};
	int maxweight = 9;

	System.out.println(knaspace(weight, value, maxweight));
		
}

}
结果打印:12

代码有一个比较难的我觉得是二重循环遍历那里,其实这些循环都是在填表,得出这张状态表,二重循环就是一行一行的求,先求i=1时每个承重量的最大价值,再求2,3,4,就一行行的求。

具体的重量价值和背包最大承重量也是基于开始博客的那个地址给出的数据。
即四个物体,重量和价值分别是:(2,3),(3,4),(4,5),
(5,7),背包最大承重量为9。

下面的图片是遍历数组时得到的各个状态值,每一个相当于是地基,根据地基才能得出最终的结果。


在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值