【人话聊算法】背包问题集合

背包问题总结

01背包完全背包多重背包
要求填满略微变动略微变动转化01背包
不要求填满基础基础转化01背包


问题模型:
有若干种物品,每种物品有特定的价值和所需要的空间。
要求,在不超过空间的情况下求出最大的价值总和(注意不是方法数,如果说是方法数可用母函数解决)。
核心模板:

for (int i = 1; i <= n; i++)//这里的n是物品的种类
{
    scanf("%d %d", &j, &f);//这里f读取的是所需要的空间,也就是消耗的资源,j读取的是价值,这里边读边更新
    for (int k = m; k >= f; k--)//此处k是放置每一种物品时候的各个大小背包的空间
    {
        dp[k] = max(dp[k], dp[k - f] + j);//一维数组最强,读取位置左侧为i - 1右侧为i,从左到右是完全背包,从右到左是01背包
    }
}


模板理解:
1、一维数组是二维数组的空间压缩版本,本质上是相同的。

2、先看模板。

第一步:我们针对每一种物品进行动态规划。

第二步:我们读取这一种物品的数据情况,价值和数量。

            注意:如果不是一个一个输的,我们可以先存在两个一维数组当中,再调用。

第三步:我们针对这一种物品,考虑目标背包的子背包的最优方案,动态规划的思想。

第四步:状态转移方程,也就是递推公式。

             观察递推。

             a.其中dp[k] = max(dp[k],....),是在利用各个dp[k]存储各个可能值的最大最大值,在dfs的路               径搜索的时候也是经常使用的

             b.我们要理解,dp[k - f] + j,我们假设在此处的dp[k]之前的问题我们都已经解决了,       d[k - f]的含义就是,当背包容量为k - f的时候,之前放置的所有种类物品的最优背包方案,相当于我们在向容量k的背包放置的时候,挪出来f的空间给这个物品,此处未更新前的dp[k]是没有考虑这个物品的最好背包,现在我们考虑要不要放这个物品,如果放这个物品我们最优方案是什么,显然空间越大价值肯定不会减小,我们希望在之前的最大的背包里放入这个物品,因此我们选刚刚好可以放入这个物品的dp[k - f]背包,这样,我们就成功更新了这个背包,转化为了考虑放入了这个物品的最好背包,从而可以作为下一步的历史数据。

3、初始化的状态,不要求填满的背包,初始化为0。

为什么呢?因为我们在考虑完第零种物品后,最好背包都是价值为0;

但是,要求填满的背包,初始化为负无穷。

为什么呢?因为我们一定要求,必须从空间为0的背包,一步一个脚印的堆叠到目标容量,不可以踩着虚空。

memset(dp, -0x3f, sizeof(dp))如此操作,并且把dp[0] = 0,因为空间为0的背包是基点。

4、关于数组滚动的方法,外层没关系,因为所有的物品都要考虑,先考虑第一种和先考虑第n种本质上是没有区别的,结果是一样的。

但是, 内层关系到背包的类型。


dp[k]

k

数组在滚动,滚动后的是更新后的地方,但是我们考虑背包容量的时候是考虑小的地方放向是一定的,我们参考的都是左侧的dp值。

当从左往右的时候,我们考虑挪出容量后的背包的话,我们参考的是已经考虑过该种物品的历史数据

这样,我们就造成了一个物品数量不限的结果,也就是完全背包。因为就算我们已经考虑过了我们还可以再次去考虑它,可以多次放置,不限次数。

当从右往左的时候,我们考虑挪出容量后背包的话,我们参考的是还没有考虑过这种物品的历史数据。

这样,我们就造成了一个数量唯一的局面,也就是01背包。因为我们最多只考虑一次,甚至不考虑放置该物品。

5、关于多重背包,我们的方法是拆解背包。

如何是有效的拆解方法呢?首先我们不能消除其在最终背包里的数目的可能性,而且要足够简便。

你可以直接单个单个拆,但是如果数组量大的时候就会TLE。

一般常用二进制拆解的方法。拆成1 2 4 8 ....个一点余数

书写方法:

int k = 1;//这是二进制拆包的开端
int cnt = 0;//这是用来统计包的数目,用来放到最后考虑的物品的种类数
while (n >= k)//n是要分解的数
{
    n = n - k;//拆去一个数
    v[++cnt] = k * a;//新的物品的体积和价值
    w[cnt] = k * b; 
    k = k * 2;//翻倍
}
if (n)//如果有剩余的话单独成包
{
    v[++cnt] = n * a;
    w[cnt] = n * b;
}

二进制拆包的神奇之处在于可以通过组合可以搭配出0~n的所有数,没有因为随意打包而导致某些可能性的缺失,而且能够大大减少后续判断的次数。

如有缺漏还请多多指教。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值