01背包问题(动态规划解决)

动态规划原理

动态规划的核心:填表

动态规划和分治法的相同点:把大问题拆分成小问题,再找大小问题间的递推关系。由最基本的小问题开始解决,逐渐解决大问题。

动态规划和分治法的不同点:分治法不会记忆中间的过程,因此需要重复计算子问题。动态规划以填表的形式将已经解决了的小问题的答案都记录下来,如有需要可随时提用,避免了重复计算,节省了时间。在问题满足最优性原理后,动态规划解决问题的核心就在于填表,表填写完毕,最优解就找到了。

最优性原理是动态规划的基础:即解决每一个小问题都采用最优的方式,因此其后的步骤可采用前面结果且不影响最优性。
(最优性原理是指“多阶段决策过程的最优决策序列具有这样的性质:不论初始状态和初始决策如何,对于前面决策所造成的某一状态而言,其后各阶段的决策序列必须构成最优策略”。:大佬原文中的解释)

问题描述

有n个物体(由输入数据组数决定),每个物体都有各自的体积和价值。现给定一容量一定的背包,求怎样装才能让背包所装总物体价值最大?
记背包容量为8,物体体积,价值如下。(以输入4组数据为例)

物体编号体积价值
123
234
345
456

问题分析

从最小容量开始分析填表(0-8)以totalvalue[j][k]记录最大价值表格信息
j记录物体数,k记录背包容量。

递推关系:每个商品都有两种可能
1:包的容量比当前物体小,不装入背包:此时价值与前j-1个相同,totalvalue[j][k]=totalvalue[j-1][k]
2:包的容量能装下当前物体,装入背包:此时价值等于背包剩余容量(背包当前总容量减去当前所装入物体的体积)最大价值+当前物体价值

因此填入totalvalue表时需对上述两种情况进行比较,取最大价值记录。

填表分析
如背包容量为0时,不论有几个物体待选,总价值都为0。

背包容量为1时,没有能装下的物体(物体体积,价值见上),总价值也为0

例背包容量为4时:当只有0个物体时,总价值为0。
当有第一个物体(体积2,价值3)能装时,4>2,装价值为3,不装价值为0,3>0所以选择装,最大价值为3。【列数4,行数1】填3

当有第二个物体(体积3,价值4)也能装时,4>3,不装,价值和只有第一个物体能装时相同,为3,装则价值=4(当前物体价值)+背包容量为1(当前背包总容量(4)-当前物体体积(3))时的最大价值(0)=4,4>3,所以选择装,最大价值为4。【列数4,行数2】填4

例题totalvalue表格如下

物体/背包容量012345678
0000000000
1003333333
2003447777
3003457899
40034578910

填完表格后最优解即为totalvalue[8][4] (所给背包容量,物品数对应的结果)

找最大价值物体组成(回溯)
背包容量一定,从最多物体开始分析
比较totalvalue[j][k]和totalvalue[j-1][k],若两者不相等,则说明当前物体被装入了背包,于是将k-当前物体的体积=k2,再比较totalvalue[j][k2]和totalvalue[j-1][k2]遍历直到k=0,由此判断背包剩余体积组成。

代码实现

#include<iostream>
using namespace std;	
int totalvalue[1005][1005]={0};
//定义较大数组需放在main外做为全局变量 ,否则会导致程序无法输入直接终止的问题 

int max(int m,int n)
{
	return m>n?m:n; 
}//比较取大函数 

int main()
{
	int totalspace;
	int m,n;//m-space,n-value
	int space[1005],value[1005];

	int i=1,j=0,k=0;
	cin>>totalspace;
	while(cin>>m>>n&&m&&n)
	{
		space[i]=m;
		value[i]=n;
		i++;//记录输入数据组数 
	}
	for(j=1;j<i;j++)
	{
		for(k=1;k<=totalspace;k++)
		{
			if(k<space[j])//背包容量小于物体体积,未装入 
			    totalvalue[j][k]=totalvalue[j-1][k];
			else//当能装下时,需进行比较决定装或不装 
		        totalvalue[j][k]=max(totalvalue[j-1][k],totalvalue[j-1][k-space[j]]+value[j]); 
		}
	}//填totalvalue[][]这个表 ::重点 
	for(j=0;j<i;j++)
	{
		for(k=0;k<=totalspace;k++)
		    cout<<totalvalue[j][k]<<' ';
		cout<<endl;
	} //输出动态规划表(totalvalue) 
	cout<<endl; 
	for(j=i-1;j>1;j--)
	{
		if(totalvalue[j][totalspace]!=totalvalue[j-1][totalspace])
		{
			cout<<j<<' ';
			totalspace-=space[j];//改变容量以找出下一个组成部分 
		}    
	} //输出最优解组成 
	cout<<endl;
	return 0;
}

为了学明白这个问题,我看了B站的视频讲解,也看了大佬的分析文章,此处附上大佬文章链接https://blog.csdn.net/qq_37767455/article/details/99086678?

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值