前言

看了 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. 明解STM32—GPIO理论基础知识篇之寄存器原理

    ​ 一.前言 在之前的STM32的GPIO理论基础知识中,分别对基本结构和工作模式进行了详细的介绍.GPIO基本结构中主要对GPIO内部的各个功能电路逐一的进行的分析:GPIO工作模式中主要介绍GPI ...

  2. Proxmox VE镜像分析与定制

    Proxmox VE(Proxmox Virtual Environment,简称PVE)是一个开源的服务器虚拟化环境Linux发行版,基于Debian,使用给予Ubuntu的定制内核.相比于其他虚拟 ...

  3. 【Diary】CSP-S 2020 游记

    一年 好快 从三百多天倒计时 一点一点掂着 又回来了 但是时间永远不会等待你. --??? CSP-J1/S1 CSP-J1/S1 Day0 请了一上午假. 这段时间都在摸鱼,作业没写( 多备赛一个上 ...

  4. kali linux 基本渗透测试流程

    渗透测试流程 1. 信息收集阶段 网络拓扑结构分析 使用nmap扫描目标网络,获取目标主机IP地址和开放端口信息 使用whois查询目标域名的注册信息和DNS服务器信息 使用nslookup查询目标域 ...

  5. JMeter-BeanShell预处理程序和BeanShell后置处理程序的应用

    一.什么是BeanShell? BeanShell是用Java写成的,一个小型的.免费的.可以下载的.嵌入式的Java源代码解释器,JMeter性能测试工具也充分接纳了BeanShell解释器,封装成 ...

  6. 【Vue】二

    一个第三方js处理类库 BootCDN Vue过滤器 Date.now() 获取时间戳 Date.now() 1652411231222 计算属性实现 <body> <div id= ...

  7. 【SpringMVC】(二)RESTFul

    RESTFul RestFul简介 REST:Representational State Transfer,表现层资源状态转移 资源:资源是一种看待服务器的方式 资源的表述:资源的表述是资源在某个特 ...

  8. KK 与答辩

    KK 与答辩 解读一下题:如果在所有场的答辩中,有某个人的总分都要低于kk的总分,就说kk碾压该人 --> 如果在某场答辩中这个人的总分大于kk,那么就说明kk不能碾压该人. 思路就清晰了,我们 ...

  9. HDCTF_2023

    pwnner 附件 有后门函数,seed是一个固定值, //伪随机数 #include <stdio.h> #include <stdlib.h> int main() { i ...

  10. #Python 利用pandas 合并csv/xlsx文件

    上次我们分享了利用powerquery来合并文件进行数据分析,但是Pq有一部分局限性,在现实工作中,我们往往需要合并多个文件去处理数据, 如果面对20个甚至更多的文件,pq中的每一步的步骤都会去读取每 ...