01背包与完全背包一维数组的对比

本文通过代码对比解析了01背包与完全背包的区别,重点介绍了两种背包问题的状态转移方程及其实现细节,包括如何正确设置循环方向以确保正确的状态转移。

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

01背包与完全背包一维数组的对比:

在这我就废话少说,直接贴上代码供我们来比较一下啊。

代码一:声明一个 大小为  dp[ c ] 的二维数组,表示对于前i个物品,能获得的最大容量(也可以看成从前往后推,那么我们可以很容易找出它的状态转移方程

dp[ c ] = max(dp[ c ],dp [j-w[ i ]]+v[ i])   (j>=w[ i ])

 

for(int i=1;i<=n;i++)
        for(int j=c;j>=w[i];j--) ///c为总容量
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
     printf("%d\n",dp[c]);

这里j是从c->w[i](从大到小),因为只有这样我们才能够保证再前i中,(我们先假设大j>小j)每次的dp[大j]不会影响接下的dp[小j],因为接下的小j无论它怎么减,都不会与此次前面的大j,因为相等而搭上一丝丝的瓜葛。每一次的dp[j]都要保证是从前i个推来的。要是我们调换顺序的话,我们可以想下,那按这种情况的话,j是从w[i]->c(从小到大)后面的大j是不是比前面的小j大,那这样的话,假如此次前面的dp[小j]已经更新了,那这样就很有可能会影响后面的dp[大j](因后面的大j减去w[i]时可能会和此次前面更新过的小j相等而可能发生更新,这样就不满足此次大j(后面)是从前i次推过来的了,因为更新的条件是:每次更新都是不能被此次前面的更新的影响下进行的,


嗯,再接着完全背包说说:

for(int i=1;i<=n;i++)
        for(int j=w[i];j<=c;j++) ///c为总容量
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
     printf("%d\n",dp[c]);

这里j是从w[i]->c(从小到大),啊,我猜你们听了我上面说的可能大概或许有点懵,那我就举个特例来解释这个完全背包的代码吧,从这两个代码我们可以知道,只是j的循环方向不同而已,现在我们就假设i就等于1,w=3,v=4,c=15,

现在我们只要小推以下就能明白为什么要从小到大了,如果是从大到小,最后结果是4,如果从小到大,结果就是20

因为完全背包的条件是:每次更新都是可以被此次前面的更新的影响下进行的。


 

 

 

 


<think>嗯,我现在需要了解C++中的01背包问题的一维数组解法。我之前学过动态规划,但可能对优化空间复杂度不太熟悉。首先,我得回忆一下01背包问题的基本概念。 01背包问题应该是给定一组物品,每个物品有重量和价值,在背包容量限制下选择物品使得总价值最大,且每个物品只能选一次。传统的二维动态规划方法是用dp[i][j]表示前i个物品在容量j时的最大价值。状态转移方程大概是dp[i][j] = max(dp[i-1][j], dp[i-1][j - weight[i]] + value[i])。这样需要O(n*capacity)的空间,当容量很大时可能会占用较多内存。 现在问题是优化到一维数组。用户想知道如何用一维数组实现。那怎么压缩空间呢?我记得可能需要改变遍历的顺序。比如,二维情况下,每个状态依赖于上一行的左边状态,所以如果一维的话,可能需要逆序遍历容量,这样在更新的时候不会覆盖掉之前需要用的数据。 比如,假设用一维数组dp[j],其中dp[j]表示容量为j时的最大价值。初始化时,所有dp[j]都是0。然后对于每个物品,从后往前遍历容量,从capacity到weight[i],这样在更新dp[j]的时候,用到的dp[j - weight[i]]还是上一轮的结果,也就是没有选当前物品的状态。这样就能保证每个物品只被选一次。 举个例子,比如当前处理第i个物品,重量是w,价值是v。在二维数组中,处理第i行的时候,每个dp[i][j]依赖于dp[i-1][j]和dp[i-1][j - w] + v。而一维的情况下,如果正序遍历,那么前面的j - w可能已经被更新过了,导致重复选物品,这不符合01背包的要求。所以必须逆序处理容量。 那具体的代码结构应该是怎样的呢?大概的步骤是: 1. 初始化一个一维数组dp,大小为capacity+1,初始值为0。 2. 遍历每个物品,对于每个物品i,遍历j从capacity到weight[i](包括weight[i]),因为如果j < weight[i],当前物品放不下,不需要更新。 3. 对于每个j,dp[j] = max(dp[j], dp[j - weight[i]] + value[i])。 4. 最终dp[capacity]就是最大价值。 那这个一维数组的时间复杂度应该还是O(n*capacity),但空间复杂度降到了O(capacity),这确实节省了很多空间,特别是当capacity很大时,比如上百万的时候,二维数组可能无法处理,而一维数组可能可以。 需要注意的问题可能有:遍历的顺序必须是逆序的,否则会导致完全背包问题的效果(即物品可以被选多次)。还有,初始化的时候dp[0]是0,其他可能也是0,或者根据问题要求是否恰好装满的情况可能有所不同。比如,如果要求恰好装满,可能需要将dp初始化为负无穷,只有dp[0]为0,这样只有在恰好装满的情况下才有解。 比如,当题目是恰好装满背包的最大价值时,初始化可能不同。但通常情况下,01背包问题可能允许不超过容量,这时候初始化为0即可。 举个例子,假设物品有重量数组weights和值数组values,容量为cap。那么代码大概是这样的: vector<int> dp(cap + 1, 0); for(int i = 0; i < n; ++i){ for(int j = cap; j >= weights[i]; --j){ dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); } } return dp[cap]; 这样就能得到最大价值。那这应该就是正确的一维数组实现方式。 总结来说,01背包一维数组解法的关键点在于逆序遍历容量,确保每个物品只被考虑一次。这样可以节省空间,同时保持正确性。</think>在C++中,**01背包问题的一维数组解法**通过优化动态规划的空间复杂度,将原本的二维数组压缩为一维数组,从而更高效地解决问题。以下是核心要点和实现步骤: --- ### **核心思想** - **状态定义**:`dp[j]` 表示容量为 `j` 的背包能装下的最大价值。 - **逆序遍历**:为避免重复选取物品(保证每个物品仅选一次),容量需从大到小遍历。 - **状态转移**:`dp[j] = max(dp[j], dp[j - weight[i]] + value[i])`,决策是否选择当前物品。 --- ### **实现步骤** 1. **初始化数组** 创建一维数组 `dp`,大小为 `capacity + 1`,初始值全为0(若要求恰好装满背包,需初始化为负无穷,仅 `dp[0] = 0`)。 2. **遍历每个物品** 对每个物品的重量 `weight[i]` 和价值 `value[i]`,从背包容量最大值逆向更新到当前物品重量。 3. **更新状态** 通过比较“不选当前物品”和“选当前物品”的价值,更新 `dp[j]`。 ```cpp #include <vector> using namespace std; int knapsack01_1D(vector<int>& weights, vector<int>& values, int capacity) { vector<int> dp(capacity + 1, 0); int n = weights.size(); for (int i = 0; i < n; ++i) { for (int j = capacity; j >= weights[i]; --j) { dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); } } return dp[capacity]; } ``` --- ### **关键点** - **逆序原因**:正序遍历会导致小容量状态被覆盖,从而允许重复选择物品(变为完全背包问题)。 - **空间优化**:时间复杂度仍为 O(n·capacity),但空间复杂度从 O(n·capacity) 降至 O(capacity)。 - **应用场景**:适用于物品数量多或背包容量较大的情况,如竞赛题目或实际工程中的资源分配问题。 --- ### **示例说明** 假设物品信息如下: - 重量:`weights = {2, 3, 4, 5}` - 价值:`values = {3, 4, 5, 6}` - 背包容量:`capacity = 8` 最终 `dp[8]` 的值为最大价值(此例为10,选择重量3和5的物品)。 通过一维数组解法,既能高效计算最大价值,又显著节省内存空间。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值