【动态规划】背包问题

动态规划

定义

动态规划(Dynamic Programming)是一种分阶段求解决策问题的数学思想,它通过把原问题分解为简单的子问题来解决复杂问题.动态规划在很多领域都有着广泛的应用,例如管理学,经济学,数学,生物学。

动态规划适用于解决带有最优子结构子问题重叠性质的问题。
最优子结构 : 即是局部最优解能够决定全局最优解(也可以认为是问题可以被分解为子问题来解决),如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质。
子问题重叠 : 即是当使用递归进行自顶向下的求解时,每次产生的子问题不总是新的问题,而是已经被重复计算过的问题.动态规划利用了这种性质,使用一个集合将已经计算过的结果放入其中,当再次遇见重复的问题时,只需要从集合中取出对应的结果。

步骤

1.确定状态(两个核心:1最后一步 2化成子问题)
2.转移方程
3.开始和边界条件
4.计算顺序

与分治算法的区别

相同点
1.分治算法与动态规划都是将一个复杂问题分解为简单的子问题。
2.分治算法与动态规划都只能解决带有最优子结构性质的问题。
不同点
1.分治算法一般都是使用递归自顶向下实现,动态规划使用迭代自底向上实现或带有记忆功能的递归实现。
2.动态规划解决带有子问题重叠性质的问题效率更加高效。
3.分治算法分解的子问题是相对独立的。
4.动态规划分解的子问题是互相带有关联且有重叠的。

01背包

描述

已知:有一个容量为V的背包和N件物品,第i件物品的重量是w[i],收益是v[i]。
限制:每种物品只有一件,可以选择放或者不放。
问题:在不超过背包容量的情况下,最多能获得多少价值或收益。

思路

01背包的特点:每种物品只有一件,可以选择放或者不放。
用子问题定义状态:即dp[i][j]表示前i件物品放入一个容量为j的背包可以获得的最大价值。
则其状态转移方程为:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])

为了更好地理解这个转移方程,看一个具体的例子。有5个物品,编号1-5,重量分别是[2,2,6,5,4],价值分别是[6,3,5,4,6],背包的总承重为10。
列出本问题中所有的状态变量,也就是转移方程中的dp变量,且初始化为0。当第一个物品放入背包,因为第一个物品重量为2,价值为6,只要背包容量大于等于2,都可以放第一个物品,根据我们的转移方程dp[i][j] = max(dp[i -1][j], dp[i-1][j - w[i]]+v[i]),那么:
图一

根据我们的转移方程继续放第二个物品,容量为2,3的时候: dp[i- 1][j -w[i]]+v[i] < dp[i - 1][j],则dp[i][j]=dp[i - 1][j]。
容量大于3的时候: dp[i - 1][j -w[i]]+v[i] > dp[i - 1][j],则dp[i][j] = dp[i - 1][j - w[i]]+v[i]。dp变量被更新为︰
图二

当放第3个物品的时候,第三个物品的重量w[i]=6,所以在背包容量j < w[i]的时候,dp[i][j] = dp[i - 1][j].
图三

以此类推,把剩余的物品全部根据转移方程放入背包后,dp变量为:
图四

通过上面的表格,可以知道当这5个物品放入容量为10的背包中,最大的价值为15,即dp[5][10] = 15。

程序实现

for (int i = 1; i <= N; ++i) {
    for (int j = 0; j <= V; ++j) {
        if(j >= w[i]) {
            dp[i][j] = max(dp[i - 1][j - w[i]] + v[i], dp[i - 1][j]);
        }
        else {
            dp[i][j] = dp[i-1][j];
        }
    }
}

完全背包

描述

当前有N种物品,第i种物品的体积是ci,价值是wi。
每种物品的数量都是无限的,可以任意选择若干件,我们称之为完全背包问题。
现有容量为V的背包,请你放入若干物品,使总体积不超过V,并且总价值尽可能大。

步骤

考虑到第i种物品最多选V/c[i]件,于是可以把第i种物品转换为V/c[i]件费用及价值均不变的物品,然后求解这个01背包问题。但是这样完全没有改进时间复杂度,但这毕竟给了我们将完全背包转换为01背包问题的思路:将一种物品拆成多件物品。
每种物品可以取0件,1件,2件,3件,甚至更多。
我们仍然可以写出状态转移方程:
dp[i][v] =max(dp[i][v - ci] + wi, dp[i - 1][v])
更简单的:
dp[v] = max(dp[v - ci] + wi , dp[v])

程序实现

// 完全背包:一维法-正序
	public static int bag3(int W, int[] w, int[] v) {
		int n = w.length - 1;// 第一个值,不算
		int[] f = new int[W + 1];
		for (int i = 1; i <= n; i++)
			for (int j = w[i]; j <= W; j++)
				f[j] = Math.max(f[j], f[j - w[i]] + v[i]);
		return f[W]; // 最优解
	}

多重背包

描述

有N种物品和一个容量为V的背包。第i种物品最多有n[i]件,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

步骤

多重背包问题的思路跟完全背包的思路非常类似,只是k的取值是有限制的,因为每件物品的数量是有限制的,状态转移方程为:
dp[i][v] = max{dp[i - 1][v - k * c[i]] + w[i] | 0 <=k <= n[i]}

程序实现

// 多重背包:一维法
	public static int bag4(int W, int[] w, int[] v, int[] num) {
		int n = w.length - 1;// 第一个值,不算
		int[] f = new int[W + 1];
		for (int i = 1; i <= n; i++)
			for (int j = W; j >= w[i]; j--)
				for (int k = 0; k <= num[i] && j - k * w[i] >= 0; k++) {
					f[j] = Math.max(f[j], f[j - k * w[i]] + k * v[i]);
				}
 
		return f[W]; // 最优解
	}

文章引用

时间太长,引用文章已忘,如有侵权将会立即删除。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值