完全背包问题

本文详细介绍了完全背包问题与01背包问题的区别,并提供了两种代码实现:朴素版和优化版。核心思路包括循环结构的状态转移方程,优化版代码通过顺推更新状态,实现了更高效的空间利用。文章适合已掌握01背包问题的读者进阶学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

没有接触过01背包的不推荐先搞别的背包问题,思路可能会不清楚或绕在一起,效果反而不好,建议先理解透彻01背包01背包问题

完全背包问题

样题:完全背包问题

先来分析一下完全背包问题与01背包问题的相同点和不同点:

01背包完全背包
物品只有 1 1 1物品有 ∞ \infty (无限,不懂数学的小伙伴们要加油了)件(并不是真的有 ∞ \infty 件,后面会讲到代码中的上限)
物品有两个属性:重量 w w w和价值 c c c物品有两个属性:重量 w w w和价值 c c c
递推方式:逆推递推方式:顺推,以后会讲为什么

思路

好啦,那么我们从这里开始讲解思路。
首先是输入和初始化部分 (只要有手就行),这部分我就不多做赘述了。

之后是重点。

核心代码思路(朴素版)

这个其实就是吧原本的完全背包换成了01背包来求解。
首先我们来讨论在上文表格中说的 “并不是真的有 ∞ \infty 件”。事实也确实是这样,它是有上限和下限的。
下限我们都很清楚,就是 0 0 0
上限呢?
It depends.(这要看情况而定)
好了,有点扯远了( ̄ー ̄)。其实上限有一个限制条件:那就是物品总重量不超过 j j j,用条件语句写出来就是: k × w [ i ] ≤ j k\times w[i]\le j k×w[i]j,这里的 k k k指当前物品的件数,由下限一直增加到上限。

循环结构

// 外面的东西
for (int k = 0; k * w[i] <= j; ++k) {
	// 状态转移方程
}
// 输出答案

状态转移方程

01背包没啥区别,但是要注意减去的重量是 k k k倍的,增加的价值也是 k k k倍的。
状态转移方程如下:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − k × w [ i ] ] + k × c [ i ] ) dp[i][j]=max(dp[i-1][j],dp[i-1][j-k\times w[i]]+k\times c[i]) dp[i][j]=max(dp[i1][j],dp[i1][jk×w[i]]+k×c[i])

代码

#include <iostream>
using namespace std;

int main()
{
    int n, m;
    cin >> m >> n;
    const int N = n + 1, M = m + 1;
    int w[N], c[N], dp[N][M];
    for (int i = 1; i < N; ++i) cin >> w[i] >> c[i];
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < M; ++j)
            dp[i][j] = 0;
    for (int i = 1; i < N; ++i)
        for (int j = m; j >= 1; --j)
            for (int k = 0; k * w[i] <= j; ++k)
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - k * w[i]] + k * c[i]);
    cout << dp[n][m];
}

核心代码思路(优化版)

压缩数组

这次我们还是通过压缩数组和状态转移方程来优化。
实际上我们可以顺着递推,也就是需要覆盖数据。
为什么呢?因为我们希望放的东西累加起来,所以不能使用旧数据,而是使用累加过的新数据。
这样就完成了压缩数组。

状态转移方程式(优化版)

状态转移方程如下:
d p [ j ] = m a x ( d p [ j ] , d p [ j − w [ i ] ] + c [ i ] ) dp[j]=max(dp[j],dp[j-w[i]]+c[i]) dp[j]=max(dp[j],dp[jw[i]]+c[i])
是的,你没有看错!
这就是妥妥的01背包状态转移方程!!!
可是注意区别:

01背包完全背包
逆推,使用旧数据,不会累加价值顺推,使用新数据,会累加价值

代码(优化版)

#include <iostream>
using namespace std;

int main()
{
    int n, m;
    cin >> m >> n; //size, numbers of things
    const int N = n + 1, M = m + 1;
    int w[N], c[N], dp[M];
    for (int i = 1; i < N; ++i) cin >> w[i] >> c[i];
    for (int i = 0; i < M; ++i) dp[i] = 0;
    for (int i = 1; i < N; ++i)
        for (int j = w[i]; j <= m; ++j) dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
    cout << "max=" << dp[m];
}

世界因为代码变短、变有用、变漂亮、变得能够输出正确答案而明亮起来 (*´゚∀゚`)ノ


有如果任何问题或建议欢迎在评论区指出(~ ̄▽ ̄)~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值