01背包问题

现在n件物品和一个总容量为C的背包,n件物品的重量分别是{w1,w2,…,wn},n件物品的价值分别为{v1,v2,…,vn},如何组合使得背包中所有物品的总价值最大?

01背包问题本质上是穷举背包容量和可供选择的物品(意思是里面的物品可能会放进背包,可能不会放进背包),取得最优解,只不过在穷举的过程中,会根据状态转移方程,只计算可能获得的最优解的部分,不去计算不是最优解的部分。具体来看,解题思路是把该问题分解为一个一个的小问题,一步步的通过小问题的最优解,最终得到大问题的最优解,跟我们人脑解题的思路是一样的。
(引自:https://www.cnblogs.com/arsenalfaninecnu/p/8945548.html)
一、二维数组表示状态方程,正序求解
我们用dp[i][j]来表示当前背包容量为j时,前i件物品所能组合出来的最大价值,现在需要考虑的就是第i件物品放还是不放?
情况①:第i件物品的重量比背包剩余容量要大,那肯定是放不下的
情况②:第i件物品的重量比背包剩余容量小,那么就要考虑放进去的收益大,还是不放进去的收益大
由此得到了状态转移方程

if(j<w[i])
	dp[i][j]=dp[i-1][j];
if(j>=w[i])
	dp[i][j]=Math.Max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);

二、压缩空间,使用一维数组表示状态方程,逆序求解
从上面的状态方程可以看出,选择放第i件物品时的状态,仅仅与选择放第i-1件物品的状态相关,那么就可以去掉dp的数组的第一纬度(i),压缩为一维数组,然后从选择第一件到最后一件物品之间,不断更新这个一维数组即可。
即dp[j]表示某状态时,当背包容量为j时所得到的最大价值。
先给出状态转移方程:

if(j<w[i])
	dp[j]=dp[j]
if(j>=w[i])
	dp[i][j]=Math.Max(dp[j],dp[j-w[i]]+v[i]);

仍然是双层循环,第一层为从第1件物品到最后一件物品,选择放还是不放,不同的是,第二层循环需要逆序。
因为每进行一次外层循环,我们都需要求出放前i-1件物品时的最佳选择,i时刻的dp[j]是和i-1时刻的dp[j-w[i]]密切相关的,如果内层循环选择正序的话,当求到dp[j]时,我们会发现dp[j-w[i]]的状态不再是i-1时刻的状态,而是i时刻的状态了,连锁反应,造成后面的结果均不正确。

三、代码

public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();  //物品数量
		int C = in.nextInt();  //背包容量
		int[] value = new int[n+1];  //物品的价值	
		int[] weight = new int[n+1]; //物品的重量
		for(int i=1;i<=n;i++)
		{
			weight[i]=in.nextInt();
			value[i]=in.nextInt();
		}
		
		/*一维数组逆序解法*/
		int[] dp = new int[C+1];
		Arrays.fill(dp, 0);  //i=0时刻dp的状态,一件物品也没有,所以价值为0
		for(int i=1;i<=n;i++)
			for(int j=C;j>=1;j--)
			{
				if(j<weight[i])  //背包容量放不下第i件物品
					dp[j]=dp[j];  //i状态和i-1状态相同,即没有放第i件物品
				else
					dp[j]=Math.max(dp[j], dp[j-weight[i]]+value[i]);  //能放下的时候,选择放与不放
			}
		System.out.println("(逆序解法)背包物品的最大总价值为:"+dp[C]);		
			
		/*二维数组正序解法	*/
		int[][] dp1 = new int[n+1][C+1];  //当前背包容量为j的时候,放置i个物品所能组成的最大价值。
		Arrays.fill(dp1[0], 0);	//一个物品也没有,总价值为0
		for(int i=1;i<=n;i++)
			for(int j=1;j<=C;j++)
			{
				if(weight[i]>j)  //放不下第i个物品
				{
					dp1[i][j]=dp1[i-1][j];
				}
				else
				{   //选择放第i个物品还是不放
					dp1[i][j]=Math.max(dp1[i-1][j], dp1[i-1][j-weight[i]]+value[i]);
				}
			}
		System.out.println("(正序解法)背包物品的最大总价值为:"+dp1[n][C]);		
	}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值