第十七天:动态规划之背包型动态规划

今天是释然发题解的第十七天,以后每一天都会和大家分享学习路上的心得,希望和大家一起进步,一起享受coding的乐趣。
本文约2500字,预计阅读8分钟
昨天我们学习了动态规划,忘记的小伙伴们可以看一下哦:

动态规划

今天我们来聊一聊动态规划的背包型动态规划,明天和大家分享动态规划之线性动态规划的相关内容:

例题:从记忆化搜索写起

01 背包问题
有n个重量和价值分别为 Wi,Vi,的物品。从这些物品中挑选出总重量不超过 W的物品,求所有挑选方案中价值总和的最大值。
限制条件
• 1 <= n <=100
. 1 <= Wi,Vi <=100
. 1 <= W <=10000
我们在不知道背包型动态规划之前呢,我们考虑装n个物体,如果使用搜索时间复杂度就是2^n,

//明白dfs的含义是:搜索到第i个位置时,剩余空间为left时
//可以装的物品的最大价值,那么我们就可以不需要通过传入其它参数去不停地递归调用:
int dfs(int i,int left)
{
    if(i>=n+1) return 0;//回溯条件
    int dfs1=-INF,dfs2=-INF;
    dfs1=dfs(i+1,left));
    if(left>=w[i])
    {
        dfs2= dfs(i+1,left-w[i])+v[i];
    }
    return max(dfs1,dfs2);
}

总感觉这个搜索,会有多次的调用,没错,当它的参数一样的时候,那么它的值永远是固定的,那么此时我们通过记录每一次调用过后的值,就可以减少递归的次数

因此优化一下:

//我们同暂时数组来存储递归调用的值
int tem[MAX_N][MAX_N]int dfs(int i,int left)
{
	if( tem[i][tleft] != -1 ) 
		return mem[i][tleft];
    if(i>=n+1) return tem[i][left]=0;//回溯条件
    int dfs1=-INF,dfs2=-INF;
    dfs1=dfs(i+1,left));
    if(left>=w[i])
    {
        dfs2= dfs(i+1,left-w[i])+v[i];
    }
    return tem[i][left]=max(dfs1,dfs2);
}

那么这个时候看起来就会好很多,减少了所有的重复运算,并且没有额外定义的变量作为函数的参数,
自己写着写着,记忆化搜索就出来了,真有意思是不是!

总结记忆化搜索

1.明确搜索dfs的含义:

就是我们要有足够的信心相信dp数组它能够解决问题,在背包问题中呢,dfs的返回值就是我们想要的最大价值,那么不停地调用一定能够得到我们想要的解

2.记忆化搜索:

判断搜索终止的条件,每一次搜索过了之后保存过值以后避免重复搜索,并且如果向dfs传入相同的参数之后,返回值应该是相同的,因此我们就可以使用记忆化搜索

记忆化搜索和动态规划

1.根据记忆化搜索的参数可以直接得到dp的状态,反之亦然

2.根据记忆化搜索的递归关系可以写出状态转移方程,这个方程可以直接写出循环式的dp,反之亦然(但应该相反)

3.大部分记忆化搜索时空复杂度与 ** 不加优化的 ** dp 完全相同

4.两者的核心思想均为:利用对于相同参数答案相同的特性,对于相同的参数(循环式的dp体现为数组下标),记录其答案,免去重复计算,从而起到优化时间复杂度的作用

背包型动态规划总结

// 输入
int n,W;
int w[MAX_N], v[MAX_N];
// 从第i个物品开始挑选总重小于j的部分
int dfs(int i, int j) 
{
// 已经没有剩余物品了
// 无法挑选这个物品
value = dfs(i + 1, j)// 挑选和不挑选的两种情况都尝试一下
value= max((dfs(i + 1,j),dfs(i + 1, j - w[i]) + v[i]));
return value;
}

解题思路一:dp的递推的形式

递推方程这里就不给了,就是分装和不装两种情况考虑

解题思路二:动态规划的状态转移形式

前i个物品中选取总重不超过j时的状态”
向“前i+1个物品中选取总重不超过j
和前i+1个物品中选取总重不超过j+w[i]时的状态”的转移
第一个状态:
dp[i + 1][j] = max(dp[i + 1][j], dp[i][j]);
第2个状态:
if (j + w[i] <= W)
{
dp[i + 1][j + w[i]] = max(dp[i + 1][j + w[i]]# dp[i][j] + v[i]);
}

空间优化

01背包的问题
int dp[MAX_W + 1]; // DP数组
for (int i = 0; i < n; i++) {
for (int j = W; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i])
printf("%d\n" , dp[W]);
int dp[MAX_W + 1]; // DP数组
for (int i = 0; i <n; i++) {
for (int j >=w[i]; j >= w[i]; j--) 
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
printf("%d\n", dp[W])

这个代码i都是从0到n-1,指的是从放0个物体到放n个物体考虑是否需要放的问题,而差距就在j的循环上面,对于01背包问题呢,我们是需要通过之前的数据来存放这个物体是否需要放进的问题的,因此呢不能提前覆盖掉上一次的值的,但是呢对于完全背包的问题呢,因为每种物品都可以采集多次,因此不需要管之前的数据,所以递推方向刚好相反,不知道你们能不能理解,自己去推一遍,理解了其实也挺容易的

最后输出dp[i][j]就行啦!为什么是这个呢?
我们知道dp[i][j]表示的就是当物品数量为i时,空间大小为j的最大价值嘛?
题目所求的不就是这个嘛?

好了,今天的动态规划的背包型动态规划就说到这里。
释然每天发布一点自己学习的知识,希望2年后我们也能在ACM的赛场上见面,一起去追寻自己的程序猿之路吧!

后期也会和大家一起分享学习心得和学习经验呢,明天我们不见不散哦!

下期预告:

动态规划之线型型动态规划

如果大家有什么建议或者要求请后台留言,释然也想和大家一起进步呀!
联系方式:shirandexiaowo@foxmail.com

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shirandexiaowo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值