动态规划求解01背包问题

目录很重要^。^

01背包类问题说明

01背包问题求解

dp数组含义

dp数组递推公式

dp数组初始化

01背包例题 

01背包变形


01背包类问题说明

传统的背包问题

存在N件物品与承载重量为W的背包,每件物品都有自己的重量w与价值v。

每件物品只能用一次,要求计算出挑选这些物品装入背包能获得的最大价值总和。

抽象一点来说就是

一堆物品,对于这些物品中的每一个你分别选与不选?

不选就为0,丢了!选了就为1,装包!最后达成某个目的。

01背包问题求解

dp数组含义

01背包类问题,属于动态规划,因此我们设一个dp数组来求解。

dp数组是一个二维数组,你可能有点搞不清楚这是从哪跳到哪了,但是没关系,很多东西都是先背再用,首先最重要的是先把dp[i][j]的含义背下来:

dp[i][j]含义:

从下标为 0~i 的个物体中任意取后放入容量为 的背包中的最大价值总和。

从下标为 0~i 的个物体中任意取后放入容量为 的背包中的最大价值总和。

从下标为 0~i 的个物体中任意取后放入容量为 的背包中的最大价值总和。

重要的问题强调3遍,背下来!!

dp数组递推公式

把上面的含义背下来后我们可以来学习递推方程了。

前面说过,对于每一件物品我们将决定它是放还是不放。 

假设我们已经从下标为 的物品递归到下标为 的物品了。对于这个物品,我们到底是放还是不放?

那么我们将目光锁定在下标为 i 的物品,在容量为j的情况下:

1.如果不放:那么能得到的最大价值是多少?那肯定等于前面 0 ~ i-1 个物品能产生的最大价值呗。

2.如果放:想一下,我们已经把第 个放进去了,那么留给前面第 0 ~ i-1 个物品的空间就不多了(国足梗,嘿嘿),剩下的空间容量就为 j - w[i] 。所以能得到的最大价值就是 i 的价值加上剩下的空间里0 ~ i-1 这些物品能凑出的最大价值。

综合两种情况,dp[i][j]的值就是放或不放两种情况的最大值,得到递推公式:

dp[i][j]  = Math.max( dp[i-1][j] , dp[i-1][j-w[i]] + v[i] )

到了这里,可以给出dp数组的视图了。

以及dp[i][j]的图:

因此,这也决定了我们的求dp数组值的遍历顺序:

从左至右,从上至下。最后dp数组右下角的值就是我们要求的最佳值。

dp数组初始化

有了递推方程,我们只需把前面的数值初始化然后就可以遍历得到后面的值了。

从递推方程中可以看出某个点的值,都是由其左上方推出来的,那么就需要初始化上面第一排,跟左边第一列了。

对于左边第一列,意思是背包容量为0时怎么装?那肯定都装不进去呗,都为0。

对于上面第一排,表示只有下标为0的物品时,背包容量不断扩大,所以是从某一容量(这一容量也就是物品0的重量了)起,物品0都被装的进去。

01背包例题 

问题: 

力扣https://leetcode-cn.com/problems/last-stone-weight-ii/

分析:   

本文重点在01背包问题,至于怎么将问题转化为01背包问题的思路,直接借鉴大佬的思路

力扣https://leetcode-cn.com/problems/last-stone-weight-ii/solution/gong-shui-san-xie-xiang-jie-wei-he-neng-jgxik/

解法:

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		//从控制台读入数据,可以不管,只需知道最后得到nums数组即为石头的重量
        Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int nums[] = new int[n];
		int sum = 0;
		for(int i = 0;i < n;i++) {
			nums[i] = sc.nextInt();
			sum += nums[i];
		}
		sc.close();
		
		//dp数组初始化
		int[][] dp = new int[nums.length][sum/2+1];
		for(int j = 0;j < sum/2+1;j++) {
			if(j >= nums[0]) dp[0][j] = nums[0];
		}
		for(int i = 1;i < nums.length;i++) {
			for(int j = 1;j < sum/2+1;j++) {
				dp[i][j] = dp[i-1][j];
				if(j >= nums[i]){
                    dp[i][j] = Math.max(dp[i][j], dp[i-1][j-nums[i]]+nums[i]);
                }
			}
		}
		System.out.print(Math.abs(sum - dp[nums.length-1][sum/2] - dp[nums.length-1]                    [sum/2]));
	}

}

01背包变形

问题来了,怎么看出它是01背包问题变形的?

1.商品不允许重复。

2.每个商品要么选,要么不选。

那递推方程是什么呢?

dp[i][j] = sum( dp[i-1][j], dp[i-1][i-v[i]] )

那怎么初始化呢?

例如:

 注意第一排,因为我们要总价为X,也就是说要刚刚好X,所以只有总价为0时和总价为第一个物品价格时初始化为1,其他初始化为0。

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		int X = sc.nextInt();
		int M = sc.nextInt();
		int[] nums = new int[M];
		for(int i = 0;i < M;i++) {
			nums[i] = sc.nextInt();
		}
		sc.close();
		
		int[][] dp = new int[M][X+1];
		dp[0][0] = 1;
		for(int i = 0;i < X+1;i++) {
			if(i == nums[0]) {
				dp[0][i] = 1;
			}
		}
		for(int i = 1;i < M;i++) {
			for(int j = 0;j < X+1;j++) {
				dp[i][j] = dp[i-1][j];
				if(j >= nums[i]) dp[i][j]+=dp[i-1][j-nums[i]];
			}
		}
		if(dp[M-1][X] != 0) {
			System.out.println(dp[M-1][X]);
		}else {
			System.out.println(-1);
		}
	}

}

以上是目前我对01背包问题的总结,肯定有不足之处哈,见谅~

你的背包,背到现在还没烂~

你的背包,让我走得好缓慢~

posted @ 2022-06-17 22:47  东东咚咚东  阅读(87)  评论(0编辑  收藏  举报