背包优化

多重背包优化一

•看做有N组物品,放入容量为j的背包,对于每组物品,选择放入的数量k(0…n[i])
•对于第i种物品,放入容量为j的背包

f[j+k*c[i]] = max{f[j+k*c[i]],f[j]+k*w[i]}

•j= 0: c[i], 2*c[i], 3*c[i],..., n[i]*c[i]
•j= 1: 1+c[i], 1+2*c[i]...
•j= c[i]: 2*c[i],3*c[i]...
•如果f[c[i]]中放入了c[i],j=0 与 j=c[i]完全重复
•一种优化,如果f[j]被更新过了,那就跳过此次循环

多重背包优化二(仅限于可行性问题)

•和完全背包相比,物品的数量是有限的,尽量节省地使用物品i
•g[i][j],记录物品i,在容量为j,达到最优值时,使用的最少数量
•如果之前物品能够在容量为j时达到最优值,那就不用使用物品i了
•f[i][j]=max{f[i-1][j],f[i][j-c[i]]+w[i]}
•g[i][j]= 0,                   f[i][j] =f[i-1][j]
•             g[i][j-c[i]]+1,  f[i][j] = f[i][j-c[i]]+w[i]  
•fori = 1 to N
•    g[0…V] = 0
•    for j = 0 to V
•         if g[j-c[i]] + 1 > n[i] continue
•          if f[j-c[i]]+w[i] > f[j]
•               f[j] = f[j-c[i]]+w[i]
•               g[j] = g[j-c[i]] + 1
•时间复杂度O(NV)

开始以为这种想法可以用于求最值的多重背包,后来学长给出反例:

”如果 g[j - v[i]] + 1 > c[i] 
这个时候 不能从转移 dp[j - v[i]] + w[i] 
这个一定是对的么 
万一 dp[j - v[i]] 的最优值 是用了 c[i] 个得到的 
但是如果我在 dp[j - v[i]] 处用用了 c[i] - 1 个 
那个不是最优值 
但是 转移到 dp[j - v[i]] + w[i] 
可以得到比 dp[j] 更优的怎么办呢 “

...还真没办...写下交了道水题,果断WA掉了...

想想,这种转移违背了最优子结构的性质,子问题最优,但原问题不定最优


多重背包优化三

就是比较常见的单调队列优化

单调队列优化,一般就是证明,对于i ,任意的j<i,  都有F(i) > F(j),则小于i的都不用考虑,从而减少枚举的决策数

详细讲解见http://www.cppblog.com/flyinghearts/archive/2010/09/01/125555.html

更多的关于单调队列优化的见http://wenku.baidu.com/view/ef259400bed5b9f3f90f1c3a.html

### 01背包问题的优化方法 #### 空间复杂度优化 传统的二维动态规划解决01背包问题时,通常定义`dp[i][j]`表示前`i`个物品放入容量为`j`的背包中的最大价值。这种方法的空间复杂度为`O(n * C)`,其中`n`是物品数量,`C`是背包容量。 通过观察发现,在计算当前状态`dp[i][j]`时,仅依赖于上一层的状态`dp[i-1][...]`。因此可以通过将二维数组降维为一维数组来减少空间消耗。具体来说: - 使用一维数组`dp[j]`代替二维数组。 - 遍历方向改为从大到小(即从`C`到`w[i]`),这样可以确保每次更新`dp[j]`时使用的仍是未被覆盖的旧值`dp[j-w[i]]`[^2]。 最终实现的空间复杂度降低至`O(C)`。 ```cpp #include <iostream> #include <vector> using namespace std; int knapsack(int capacity, const vector<int>& weights, const vector<int>& values) { vector<int> dp(capacity + 1, 0); for (size_t i = 0; i < weights.size(); ++i) { for (int j = capacity; j >= weights[i]; --j) { dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); } } return dp[capacity]; } int main() { int n, W; cin >> n >> W; vector<int> w(n), v(n); for (int i = 0; i < n; ++i) cin >> w[i] >> v[i]; cout << knapsack(W, w, v) << endl; } ``` --- #### 时间复杂度分析 即使进行了空间上的优化,时间复杂度仍然保持不变。这是因为每件物品都需要考虑是否加入背包,并且对于每个可能的背包容量都要重新评估其最优解。因此,无论是一维还是二维形式,时间复杂度均为`O(n * C)`,其中`n`代表物品的数量,而`C`则指代背包的最大承载能力[^4]。 --- #### 初始化与边界条件处理 为了保证算法能够正常运行并得出正确结果,需注意以下几点: - 当背包容量为零(`j=0`)时,任何情况下都无法放置任何物件,故此时应设所有`dp[0]=0`; - 若无可用物项可供挑选,则不论背包容积为何种数值均无法获取收益,亦即将对应位置设定初始值为零[^3]。 --- #### 实现细节说明 在实际编码过程中需要注意几个关键点以避免错误发生: - **倒序遍历**:为了避免重复利用同一个物体多次填充背包的情况出现(这违背了题目关于“一次性选取”的规定),必须采用由高往低的方向逐一访问各个潜在装载量节点的方式来进行状态转移操作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值