完全背包问题(闫氏DP分析法)

文章介绍了动态规划在解决背包问题中的应用,包括状态表示和状态计算两个关键部分。01背包允许每个物品最多选一次,而完全背包则允许选取任意次。在完全背包问题中,需要考虑不同数量的同一件物品的组合。通过优化状态转移方程,可以避免冗余计算,提高效率。
摘要由CSDN通过智能技术生成

DP问题通常要分为两部分看待

第一类就是状态表示

通常用个数组f[i][j]来表示,这个数组主要表示的是在前i个物品中选择在体积不超过j的情况下能取得到的最大值

第二类是状态计算

我们将情况分为了在前(0,1,2,3.......i)种选择,他们在体积不超过(0,1,2,3,4.......j)的情况下所能取得到的最大值


个人理解

我一直觉得背包问题有一种特性,就是他复现并且使用了过去的情况,然后让他尝试加上一个新的值,在过去他是最大的,在现在他加上一个值那么他还是最大的

  f[i][j] = max(f[i - 1][j]        , f[i - 1][j - v[i]] + w[i];
//更新当前的值    不加入新元素的值   尝试加入新元素之后的值
因为我们一直保证f[i][j]这个权值一直是所挑选出来的最大的,要想更新他的值也要在他本来就是“老大”的基础上尝试,这样就不会漏解
  • 这里我们就复现了以前的状态:如果要加入第i个物品,如果直接算只能暴力搜索,为了追求效率,我们采用了一种“曲线救国”的方式,原先的要求是在不超过最大容量N的情况下要求他能娶到的最大值,我们可以绕弯一下,我们在不超过最大容量为N-v[i]的情况下取到的最大值,之后我们在加上去掉的第i个物体的权值就可以了。

不论是01背包还是别的背包模型都会用到这种思路,我觉得这就是对过去状态的复用
完全背包与01背包的不同

他俩之间的区别有这几点,01背包对于每个物品能选择用不用,用的话只有一个。

完全背包对于每个物体能选择用不用,用的话有无限个。

所以我们要讨论的情况就变多了


改变的状态计算

在这里f[i][j]数组依旧表示的是去前i个物品,在体积不超过j的情况下能取到的最大值

这里多的情况就是,在01背包模型里头,我们可以选择加入第i个物品,但是在完全背包问题里头我们不止要考虑加入第i个物品,我们还要考虑加几个第i个物品呢?

  • 我们用W数组来表示权值

//01背包模型的复现,我们只需要选择是否加入这一个物品就可以了,复现的一种情况。
f[i][j] = max(f[i - 1][j] , f[i - 1][j - v[i]] + w[i];
//在完全背包模型里头,我们复现的情况就变多了,我们要复现的是再加入k(k=0,1,2....)个i之前的情况
//我们最后要求的就是在所有的复现情况中的最大值也就是
f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i],f[i-1][j-2*v[i]]+2*w[i],......)
如何优化?

在这里优化方法不是硬推公式也不是加循环

我们采用了一种类似高中数列求和的一种办法,就是后退一位,找到错位之后的位置关系

     f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i],f[i-1][j-2*v[i]]+2*w[i],......)
f[i][j-v[i]] = max(          f[i-1][j-v[i]],f[i-1][j-2*v[i]]+w[i]...........

以此类推,我们发先错位之后,2式子再加上一个w[i]就是1式除了第一项剩下的那一坨!

经过优化之后我们就可以写出来之后的代码

f[i][j] = max(f[i-1][j],f[i][j-v[i]]+w[i]);

最终代码

#include<iostream>
using namespace std;
const int N = 1010;
int v[N] , w[N] , f[N];
int main(){
    int n , m ;
    cin>>n >> m;
    for(int i = 1 ; i <= n ; i ++){
        cin>>v[i]>>w[i];
    }
    for(int i = 1 ; i <= n ; i++){
        for(int j = v[i] ; j <= m ; j++){//必要条件就是当前体积必须要大于这个物体的体积才能装进去
            //因为我们用到的状态就是f[i][j-v[i]] + w[i]所以顺序遍历把最小的值先更新,正合我们意
            f[j] = max(f[j] , f[j - v[i]] + w[i]);
        }
    }
    cout<<f[m];
    return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Dou_Huanmin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值