动态规划原理
动态规划的核心:填表
动态规划和分治法的相同点:把大问题拆分成小问题,再找大小问题间的递推关系。由最基本的小问题开始解决,逐渐解决大问题。
动态规划和分治法的不同点:分治法不会记忆中间的过程,因此需要重复计算子问题。动态规划以填表的形式将已经解决了的小问题的答案都记录下来,如有需要可随时提用,避免了重复计算,节省了时间。在问题满足最优性原理后,动态规划解决问题的核心就在于填表,表填写完毕,最优解就找到了。
最优性原理是动态规划的基础:即解决每一个小问题都采用最优的方式,因此其后的步骤可采用前面结果且不影响最优性。
(最优性原理是指“多阶段决策过程的最优决策序列具有这样的性质:不论初始状态和初始决策如何,对于前面决策所造成的某一状态而言,其后各阶段的决策序列必须构成最优策略”。:大佬原文中的解释)
问题描述
有n个物体(由输入数据组数决定),每个物体都有各自的体积和价值。现给定一容量一定的背包,求怎样装才能让背包所装总物体价值最大?
记背包容量为8,物体体积,价值如下。(以输入4组数据为例)
物体编号 | 体积 | 价值 |
---|---|---|
1 | 2 | 3 |
2 | 3 | 4 |
3 | 4 | 5 |
4 | 5 | 6 |
问题分析
从最小容量开始分析填表(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表格如下
物体/背包容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
2 | 0 | 0 | 3 | 4 | 4 | 7 | 7 | 7 | 7 |
3 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 9 |
4 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 10 |
填完表格后最优解即为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?