背包图解(01背包、完全背包、多重背包)

背包状态转移方程

动态规划是求最优解的数学方法,它利用各阶段之间的关系,逐个求解,最终求得全局最优解。

笼统地来说,思路就是一个数组dp[],手动设置dp[0],递推求dp[1]...最后得dp[n]就是所求。

设计动态规划算法时,最重要的就是明确:

  1.  dp[i]表示什么?
  2.  如何使用dp[0...i-1]求得dp[i],也就是状态转移方程是什么?
  3. 边界值dp[0]是多少?

在解题找不到思路时,第一个应该思考的就是状态转移方程是什么,这个递推关系在哪里?有递推关系的状态才能作为这个动态规划算法中的状态。没有递推关系的状态是无法解题的,举个例子就是,最长上升子序列中,正确的方法是设dp[i]为以位置i为结尾的最长上升子序列长度,因为这样dp[i]可以由dp[0...i-1]获得,而如果将dp[i]设为前i个数组成的最长上升子序列长度,dp[i]与dp[0...i-1]就没有递推关系,无法求得最后结果。

背包问题:给出n件物品各自的价值和体积,和一个容量为V的背包,将物品放入背包,不能超出容量求最大价值。

作为动态规划中一类经典问题,背包问题的递推关系就是:已知前i - 1件物品放入后的最大价值,前i件物品最大价值就是第i件放与不放的区别。

01背包

①背包可以不被装满

有一个容量为V的背包,和一些物品,这些物品分别有两个属性,体积w和价值v,每种物品只有一个。

状态:dp[i][j]表示在总体积不超过j的情况下,前i个物品所能达到的最大价值

状态转移方程:dp[i][j] = max(dp[i - 1][j - w] + v, dp[i - 1][j])

最后要求的是dp[n][V],也就是体积不超过V的条件下,所有物品所能达到的最大价值

最开始能知道的是dp[0][0...V] = 0,也就是不装入任何物品的情况下,能达到的最大价值是0

for (int i = 1; i <= n; i++) { //循环每一个物品
	for (int j = 0; j < list[i].w; j++) { //可用的总体积小于物品i的体积
		dp[i][j] = dp[i - 1][j]; //物品i只能不加
	}
	for (int j = list[i].w; j <= V; j++) { //可用的总体积大于等于物品i的体积
		//取物品i加或不加的最大值
		dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - list[i].w] + list[i].v);
	}
}

i为0时的边界值已经确定,只需要遍历i,求解各状态时的最大值,最后得到的dp[n][V]即为所求,以下面的数据模拟上述递推过程。

物品序号体积价值
134
241
323
背包容量V8

                     

空间优化 

for (int i = 1; i <= n; i++) { //循环每一个物品
	for (int j = V; j >= list[i].w; j--) { //可用的总体积大于等于物品i的体积
		//取物品i加或不加的最大值
		dp[j] = max(dp[j], dp[j - list[i].w] + list[i].v);
	}
}

 将二维数组优化为一维时,本轮修改黄色方块处值,需要蓝色方块处值与上一行相同,由图中蓝黄方块位置可以看出,蓝色位置小于等于黄色,所以使用一维数组时,倒序修改数组值。比较的dp[j]实际是dp[i - 1][j],修改的dp[j]实际是dp[i][j]。

②背包必须被装满

只需要将初始状态修改为dp[0]=0,其他为负无穷。


完全背包

物品可以放任意个。只需要在01背包基础上,改为正序修改dp[j]。

 j遍历到6时,最大容量可以容纳两个物品i,而dp[6]正好由dp[3]和dp[6]决定,此时的dp[3]就是已经放入一个物品i之后的价值,dp[6]在一个物品i的基础上再加入一个物品i。这样在遍历每一个物品的过程中,通过这样的方式考虑了物品i加入任意个的情况。


多重背包

多重背包是物品只能放有限个。将物品个数按二进制拆分。

int w, v, k;
for (int i = 1; i <= n; i++) {
	cin >> w >> v >> k;
	int c = 1;
	while (k > c) {
		len++;
		k -= c;
		list[len].w = w * c;
		list[len].v = v * c;
		c *= 2;
	}
	len++;
	list[len].w = w * k;
	list[len].v = v * k;
}

参考

《王道机试指南》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值