博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【算法】背包问题概要
阅读量:5368 次
发布时间:2019-06-15

本文共 2266 字,大约阅读时间需要 7 分钟。

背包问题

简述

每个物品有自己的体积和收益。

把一堆如上的物品放进容量有限的背包,使收益最大。

分类

  • 01背包 每个物品至多放一个
  • 完全背包 每个物品有无限个
  • 多重背包 每个物品有有限个
  • 分组背包 每个物品有归属组,且每组只能选有限个。
  • 到达型背包 大概就是求解该阶段是否有符合要求的决策。
  • 毒瘤背包 将以上几种背包结♂合起来(听其他神犇说有六种背包,蒟蒻只会这几种)

(万能)状态转移方程

\(f_j\) 表示放入的物品的体积为(不超过) \(j\) 的最大收益。

\(v_i\) 表示放入第 \(i\) 个物品的收益,也可以理解为他的价值

\(w_i\) 表示放入第 \(i\) 个物品所消耗掉的体积,也可以理解为他的体积

\[f_j = max(f_j, f_{j - w_i} + v_i)\]

这个方程的意义就是:

当状态为 \(j\) 的时候,有两种途径可以获得 \(f_j\) 的值:不选这第 \(i\) 个物品(\(f_j\))和选 第\(i\)个物品(\(f_{j - w_i} + v_i\))。只需要算出这两种途径所带来的收益的最大值就可以了。

再来解释一下选第 \(i\) 个物品的状态转移方程:\(j - w_i\) 表示:当前所花的容量 \(j\) 减去当前选择的物品 \(i\) 的容量 \(w_i\) 表示不选这个物品时的最大收益。加上 \(v_i\) 的意思就是加上这个物品的价值。

即当前收益最大值可以从之前的收益最大值递推出来。

这就是适用于这几种背包的__万能__状态转移方程。

看不懂就再思考思考,玩味个几天就懂了。

实在不行背板子

那么如何循环呢?

深入了解背包(与大多数DP)的概念

这里我先 口胡 一下

明白了状转方程之后,我们要考虑如何设计循环了。

这里我们就涉及到了三个重要的概念

阶段、状态、决策

阶段:

求解一个 \(DP\) 问题通常会分为几个离散化(即不连续)的阶段(大多数情况下不连续)的阶段。

就以下文 \(01\)背包 为例。

几乎所有的背包都将选择第 \(i\) 个物品作为不同的阶段。

状态:

每一个阶段都会对应几种状态,比如下文中, \(i, j\) 就共同构成了状态。状态可以理解为该阶段下的一些值(比如边权等等)。

几乎所有的背包的状态都是物品的 \(v\)\(w\)

决策:

决策可以理解为对于该阶段的求解、转移。

决策在背包问题中(应该)只存在于分组背包与多重背包中(当然还有到达型背包)。

顺序:

个人认为所有的 \(DP\) 都遵循这个原则:

循环时,顺序为阶段 \(\rhd\) 状态 \(\rhd\) 决策

循环顺序

01背包

for (int i = 1; i <= n; ++i) {        for (int j = m; j >= w[i]; --j){            f[j] = max(f[j], f[j - w[i]] + v[i]);        }    }

如你所见, \(i\) 为阶段 \(i, j\) 共同组成了状态。决策由 \(i, j\) 组成。

第二层循环为什么要倒序呢?因为完全背包是倒序啊!

完全背包

for (int i = 1; i <= n; ++i) {        for (int j = w[i]; j <= m; ++j){            f[j] = max(f[j], f[j - w[i]] + v[i]);        }    }

为什么第二层循环是正序呢?因为 \(01\) 背包是倒序啊!

因为我们在求完全背包的时候状态是可以使用同阶段的其他状态的(即可以取一个物品无限次)。

你也可以自己手推

多重背包

for (int i = 1; i <= n; ++i) {    for (int j = 1; j <= c[i]; ++j) {        for (int k = m; k >= w[i]; --k) {            f[k] = max(f[k], f[k - w[i]] + v[i]);        }    }}

暴力的不能再暴力了

可以看出,该时间复杂度很高,为 \(O_{(n\ max(c[i])\ m)}\)

所以以后我们会遇到一个叫 二进制拆分 的一个东西,所以等我学了再来更新 \(qwq\)

分组背包

for (int i = 1; i <= n; ++i) {        for (int j = m; j >= 0; --j) {            for (int k = 1; k <= c[i]; ++k) {                if (j >= w[i][k])                    f[j] = max(f[j], f[j - w[i][k]] + v[i][k]);            }        }    }

分组背包是一个十分毒瘤的东西,我们在记忆的时候一定要记住,分组背包的状态是使用的 \(w\), 空间,而决策是使用该组的第几个物品。

其他背包

到达型背包

这个要视题目而论了。

待更新

总结

背包是一个博大精深的东西,希望各(自己)位好好学习!!!

THE END

感谢你看到这里!

转载于:https://www.cnblogs.com/blackwhitetony/p/10340491.html

你可能感兴趣的文章
python中的网页标签等字符处理
查看>>
Linux常用命令(十二)
查看>>
Linux常用命令(十五)
查看>>
Linux常用命令(十四)
查看>>
Linux常用命令(十七)
查看>>
Linux常用命令(十六)
查看>>
day 3 修改haproxy.cfg 作业
查看>>
sim usim Uim 区别
查看>>
网页中插入透明Flash的方法和技巧
查看>>
动态内存申请函数选择(realloc、malloc 、alloca、 calloc)
查看>>
获取元素属性get_attribute
查看>>
Python/jquery
查看>>
【BZOJ】【2132】圈地计划
查看>>
Java有没有goto?
查看>>
求不相邻金币相加和的最大值--动态规划1
查看>>
[转][osg]探索未知种族之osg类生物【目录】
查看>>
四十九. Zabbix报警机制 、 Zabbix进阶操作 、 监控案例
查看>>
元类中__new__ 与 __init__的区别--day27
查看>>
占小狼的简书博客
查看>>
struts2__action执行顺序
查看>>