动态规划之0-1背包问题(思路详解+表格演示过程+最优解打印方法+详细代码)

问题简介

输入:n种物品和一个背包

物品i的重量是wi,价值为vi

背包的容量是C

输出:装入背包的物品

优化目标是:装入背包的物品总价值最大

优化子结构

设(x1,x2,x3…xn)是0-1背包问题的一个最优解。

如果x1=1,那么(x2,x3,x4,x5…xn)是以下子问题的最优解:
在这里插入图片描述
如果x1 =0:则 (x2, …, xn) 是以下子问题的最优解
在这里插入图片描述

递归关系

设m(i, j)为背包问题的最优值,这里背包容量为j,可选物品为i, i+1, …, n,

有如下递归关系:

如果i<n时
在这里插入图片描述

i=n时

在这里插入图片描述

优化子结构

在这里插入图片描述

表格举例

给出如下三个物品,背包容量为5:
在这里插入图片描述

构造下列表格:
在这里插入图片描述
其中m[i][j]表示放入0-i的物品,背包容量为j的价值。

现在考虑放入id为0的物品,因为物品重量为1,所以在背包容量为0时无法放入,其他情况下都可以放入物品0,所以表格变为:
在这里插入图片描述
第二行时,要考虑放入0,1两个物品了,所以需要用到我们的迭代公式,

例如m[1][0],此时的背包容量为0,则什么都放不下为0,

而m[1][1]时,j<w1(物品1的重量为2),所以此时m[1][1]=m[0][1]=6;

再例如m[1][2]时,j>w1,此时m[1][2]=max{m[0][2],m[0][0]+v2}=max{6,0+10}=10

按照这个理论挨个填写,得到结果如下:

在这里插入图片描述
此时的m[2][5]是最佳的价值,但是如何求出最优解呢?

我们回顾m[2][5]的得到过程,m[2][5]=m[1][2]+v2=m[0][0]+v1=v1+v2,因此最佳解为放入物品1和物品2。

具体在算法中如何实现呢?我们选择利用上述的二维数组进行逆推,从最大可装入价值处逆推。

最大可装入价值处容量为5,减去最后放入的物品2的重量,得到5-3=2,我们跳转到去掉物品2,容量为2处,也就是m[1][2]处,此时装入了物品1,而1的重量为2,去掉物品1,和容量2,剩下m[0][0],此时容量已经变为0,算法结束,也就是装入了物品1和物品2。

具体代码如下:

#include<iostream>
#include<stdio.h>
#define N 6		//物品的个数
#define W 21	//背包容量

int B[N][W] = { 0 };
int w[6] = { 0,2,3,4,5,9 };	//物品重量
int v[6] = { 0,3,4,5,8,10 };//物品价值

void knapsack()
{
	int k;	//第K个物品
	int C;	//背包剩余重量

	//填表
	for (k = 1; k < N; k++)
	{
		for (C = 1; C < W; C++)
		{
			if (w[k] > C)								//第k件物品放不进去 此时背包的价值 = 判断完上一件物品之后背包的价值
			{
				B[k][C] = B[k - 1][C];
			}
			else
			{
				int value1 = B[k - 1][C - w[k]] + v[k];	//放入第k件物品后 背包总价值 = 先给这件物品留出空间,剩余的背包大小能装进的最大价值 + 这件物品的价值
				int value2 = B[k - 1][C];				//不放入第k件物品 背包总价值 = 不用给这件物品留出空间,当前背包大小能装进的最大价值(就是判断完上一件物品之后背包的价值)

				if (value1 > value2)
				{
					B[k][C] = value1;
				}
				else
				{
					B[k][C] = value2;
					if (value1 < value2)printf("k=%d C=%d\n", k, C);
				}
			}
		}
	}
}
int main()
{
	knapsack();
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < W; j++)
		{
			printf("%4d ", B[i][j]);
		}
		printf("\n\n");
	}
	system("pause");
}

(代码摘自:https://hanquan.blog.csdn.net/article/details/89456491)

  • 7
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值