前言

看了 ShanLunjiaJian 关于这个问题的文章,是完全没看懂,沙东队爷的中枢神经内核配置把我偏序了。叉姐在下面提了个论文,论文找不到资源,谁搞到了可以 Q 我一份之类的拜谢了。然后找到了这个可能是阅读笔记或者是翻译的的东西,这下算是看懂了。

感觉还是很有意思,对于 DP 的状态设计、优化思路等都有很大启发,所以写一下。

(有单个物品重量限制的)Subset Sum 问题

ShanLunjiaJian 把这个叫做 Knapsack,我是要批判的,因为感觉上是带不了权的啊,这不是Knapsack!

那么描述一下这个问题:给定 \(n\) 个物品,每个物品有一个正整数重量 \(w_i\),保证 \(1\le w_i\le V\),其中 \(V\) 是所谓的重量限制。现在给一个容量 \(C\),取这些物品的一个子集,使得重量和不能超过这个容量,然后要求物品总重量的最大值,也就是让浪费的容量最小。

低论

这个问题显然有一个做法,叫做把它当作一个普通 Knapsack 问题,时间复杂度 \(\Theta(nC)\)。那么有意义的 \(C \le nV\)。所以其实上界是 \(n^2V\)。现在我们有高论!可以做到 \(\Theta(nV)\)。

高论

首先这个问题有一个经典贪心做法,叫做给所有元素从小到大排个序,然后选一个前缀使得再多选一个就会超过容量限制。这个做法当然是错的,但是可以基于它给出的这个既有选择方法去做调整,那么这样一来,剩余容量就是 \(\mathrm O(V)\) 的(如果大于 \(V\) 就一定可以再多选一个,矛盾了),很可做!

现在我们面临的问题是什么呢?我们的 DP 可能出现负数重量了(因为要支持取消选择一些原本选了的物品),这种条件下,要去维持我的剩余容量始终保持 \(\mathrm O(V)\),是有一点难度的。怎么办呢?

考虑一个直觉,是不是可以这样去操作:当试图去取消一个原先选的物品(也就是选负数)的时候,一定要让当前容量是超额的;当去选正数的时候,一定要让当前容量是不满的。考虑最优解是不是一定能用这种方式构造出来——显然是可以的。证明可以这样想,假设我从贪心生成的“基础解”开始,按照某个顺序去把它修正成最优解,是不是每一步都一定能按照上述规则操作。那么可以这样想:如果现在容量是超额的,但是不存在某个元素使得“最优解中没选它,当前解中选了它”,那么最优解选择的集合一定包含当前解,它的容量就一定也是超额的,矛盾,所以一定有办法去进行操作。反之亦然,因此按照这样的规则操作,必然能得到某种最优解。更棒的是,在这样操作的过程中,剩余容量始终在 \([-V, V]\) 以内!真是好极了,接下来只需要基于这种构造方案来 DP 就可以了。

先设计一个朴素 DP。设贪心得到的分界点为 \(p\),使得目前选择的集合是标号 \(\le p\) 的物品,\(S_0 = \sum\limits_{i = 1}^{p} w_i\)。那么设 \(g_{i, j, k}\) 表示右边决策到 \(i\),左边决策到 \(j\),当前剩余容量为 \(k \in [-V, V]\) 的方案是否存在。这个 DP 复杂度是 \(\Theta(n^2V)\),真是一点优化效果都没有!但是这个东西的进一步改造非常方便,这就是下一步的想法。

注意到这是一个值为 Boolean 类型的 DP,如果发现某一维具有单调性,那么就可以压缩掉这维。可以观察到:如果 \(g_{i, j, k} = 1\),那么对于任何 \(t \le j\),都有 \(g_{i, t, k} = 1\)——毕竟在左侧做出更多决策一定不会使可达集合变小。那么可以设 \(f_{i, k} = \max\{0 \le j \le p | g_{i, j, k} = 1\}\)。如果不存在这样的 \(j\),设为 \(-1\)。那么 DP 的初始条件是 \(f_{p+1, S_0} = p\)。转移如下:

\[\begin{cases}
f_{i+1, k + w_i} \gets f_{i, k}, & k \le 0 & (1)\\
f_{i+1, k} \gets f_{i, k}, & \text{any} & (2)\\
f_{i, k - w_t} \gets t - 1; & k \ge 0 \land t \le f_{i, k} & (3)
\end{cases}
\]

整体按照 \(i\) 递增转移,每个 \(i\) 按照 \(k\) 递减的方向做转移 (3) 即可。时间复杂度是 \(\Theta(n^2V)\),真是一点优化效果都没有!但是这个 DP 已经是 2D-1D 的了,进一步改造非常方便,这就是下一步的想法。

注意到只有转移 (3) 的复杂度不对,而这个转移很大程度上是重复的——某些转移过程,在 \(i = a\) 可以做,在 \(i = a - 1\) 同样可以做。那么可以尝试去掉这些转移,让本质相同的转移在最小的可以进行的 \(i\) 处去进行。注意到对于某个特定的 \(k\),关于某个特定的 \(t\) 的转移能否进行,只和 \(f_{i, k}\) 是否足够大有关,而我们有单调性 \(f_{i, k} \le f_{i+1, k}\),这是因为转移 (2) 的存在。所以转移 (3) 的条件可以改为 \(k \ge 0 \land f_{i-1, k} \le t \le f_{i,k}\)。这样一来,某个特定 \(k\) 对总复杂度的贡献是 \(\mathrm O(n)\) 的,所以总复杂度就变成了 \(\Theta(nV)\)!我们成功了!

后记

有人问有什么应用?我不知道啊。这么基础的东西一定有很多应用前景吧!(心虚)

Subset Sum 问题单个物品重量限制前提下的更优算法的更多相关文章

  1. Subset sum problem

    https://en.wikipedia.org/wiki/Subset_sum_problem In computer science, the subset sum problem is an i ...

  2. 动态规划法(三)子集和问题(Subset sum problem)

      继续讲故事~~   上次讲到我们的主人公丁丁,用神奇的动态规划法解决了杂货店老板的两个找零钱问题,得到了老板的肯定.之后,他就决心去大城市闯荡了,看一看外面更大的世界.   这天,丁丁刚回到家,他 ...

  3. Partition Equal Subset Sum

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

  4. LN : leetcode 416 Partition Equal Subset Sum

    lc 416 Partition Equal Subset Sum 416 Partition Equal Subset Sum Given a non-empty array containing ...

  5. Light OJ 1272 Maximum Subset Sum 高斯消元 最大XOR值

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u011686226/article/details/32337735 题目来源:problem=12 ...

  6. call 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法.

    call 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法. 注意:该函数的语法与 apply() 方法的语法几乎完全相同,唯一的区别在于,apply()方法接受的是一个参 ...

  7. 如何使用python在保留原excel格式的前提下插入/修改数据

    一.需求分析: 统计的报表中需要每日查询当天数据并追加到原有的excel后面. 因为原始excel格式已经设定好,如果使用xlwt,仅仅指定设定我们要插入的单元格的格式,原始数据的格式会被初始化. 所 ...

  8. a,b,c为3个整型变量,在不引入第四个变量的前提下写一个算法实现 a=b b=c c=a?(异或解决值互换问题)

    package com.Summer_0424.cn; /** * @author Summer * a,b,c为3个整型变量,在不引入第四个变量的前提下写一个算法实现 a=b b=c c=a? */ ...

  9. a,b为2个整型变量,在不引入第三个变量的前提下写一个算法实现 a与b的值互换

    package com.Summer_0424.cn; /** * @author Summer * a,b为2个整型变量,在不引入第三个变量的前提下写一个算法实现 a与b的值互换? */ publi ...

  10. 不修改模板的前提下修改VisualState中的某些值

    原文链接:不修改模板的前提下修改VisualState中的某些值 - 超威蓝火 UWP里有一件非常令人不爽的事,大部分控件只提供了Normal状态下的Background,Foreground,Bor ...

随机推荐

  1. python实现员工信息表

    学习python时,看到的一个题目第一次写博客, 有误的地方还请大佬们指正,十分感谢~要求如下'''文件存储格式如下:id,name,age,phone,job(这行不需要写)1,alice,22,1 ...

  2. VUE插值语法

    目录 基本使用 基本使用 在body中创建一个标签,一般使用div,定义好id后,在script中进行定义,在前台使用{{变量}}的形式进行调用,语法如下: <!DOCTYPE html> ...

  3. laravel实现大数据csv导出

    首先说明几点: excel格式的文件最大支持100万的数据,所以不考虑使用excel格式 laravel的toArray()方法有内存泄露,所以大量数据导出不能使用. 当然要使用chunk方法查询数据 ...

  4. python运维工程师-cmdb项目-day2

    1.捕获异常信息 import tracebackdef disk(): int('saaa')def run(): try: disk() except Exception: ret=traceba ...

  5. X配置文件xorg.conf分析

    X配置文件xorg.conf分析 转载于:http://blog.csdn.NET/comcat/archive/2007/04/02/1549658.aspx 作者:壮志凌云的csdn博客 X的配置 ...

  6. [Tensorflow]模型持久化的原理,将CKPT转为pb文件,使用pb模型预测

    文章目录 [Tensorflow]模型持久化的原理,将CKPT转为pb文件,使用pb模型预测 一.模型持久化 1.持久化代码实现 convert_variables_to_constants固化模型结 ...

  7. [OpenCV-Python] 9 图像的基础操作

    文章目录 OpenCV-Python: 核心操作 9 图像的基础操作 9.1 获取并修改像素值 9.2 获取图像属性 9.3 图像 ROI 9.4 拆分及合并图像通道 9.5 为图像扩边(填充) Op ...

  8. Spring之AOP理解及使用

    文章目录 AOP是什么 AOP Spring的通知类型 1.Before通知 2. AfterReturning通知 3.AfterThrowing通知 4. After通知 5. Around通知 ...

  9. 2022-03-17:所有黑洞的中心点记录在holes数组里, 比如[[3,5] [6,9]]表示,第一个黑洞在(3,5),第二个黑洞在(6,9), 并且所有黑洞的中心点都在左下角(0,0),右上角(

    2022-03-17:所有黑洞的中心点记录在holes数组里, 比如[[3,5] [6,9]]表示,第一个黑洞在(3,5),第二个黑洞在(6,9), 并且所有黑洞的中心点都在左下角(0,0),右上角( ...

  10. drf-spectacular

    介绍 drf-spectacular是为Django REST Framework生成合理灵活的OpenAPI 3.0模式.它可以自动帮我们提取接口中的信息,从而形成接口文档,而且内容十分详细,再也不 ...