KingdomAndDice

作者:陈俊锟 汪乐平 钟知闲 徐明宽

关键词:DP 背包 多重背包

题目简述

给出两个长度均为 的数组 ,其中 中有一些位置是 。你需要将 中若干个 修改成其他的数,要求最终的数组 满足:

  1. 中,所有数都是 之间的整数

  2. 所有正整数在中的出现次数加起来不超过

在此基础上,设 为“随机两个 之间的整数 ”后,“”这一事件发生的概率,要求选择一种修改 的方式,使得 最小。

要求返回使得 最小的 。如果存在多个 ,输出较小的那个。

算法一

首先,这两个数组的下标是没有意义的,因此我们可以把它们看成可重集合。

其次,我们可以将取到某个 的概率分别考虑。如果这个 ,那么它对 的贡献也确定了。如果不将它修改为非 的值,那么它对 没有贡献。否则,如果将 数组中的数排序,那么可以将 中分成若干段,其中每一段内取不管取哪一个数对 的贡献都是相同的。

设布尔数组 表示已经改掉了 时,是否存在一种方案使得 。我们把对 的贡献相同的数在一起考虑。那么问题就转化为了一个经典的多重背包问题,直接 DP 求解即可。

最后,我们就可以得出所有可行的 ,直接计算答案即可。

时间复杂度:

关于时间复杂度

通常采用二进制分组的方法实现多重背包,即对于 数组中 连续的一段,如果 数组中满足 个数为 ,那么相当于 个大小为 的物品,将其拆成 个大小依次为 的物品(其中整数 满足 ),然后做0-1背包。这样的物品总数为 。用上述做法的时间复杂度和空间复杂度分别为 (如果使用滚动数组)。

虽然这个做法实际上已经可以通过了(毕竟 很小),但我们有时间复杂度更优的算法。以下给出两种算法:

1、修改状态表示。设 为从前 个段(段的意义如上文所述,同一段内取每个数对 贡献相同)中选取 个数改成 并使得 ,第 个段至少要选多少个数改成 。转移时只需枚举是否从第 个段中取数,同时注意第 段已取满的状态不能再转移即可。这样的时间复杂度和空间复杂度分别为 (如果使用滚动数组将 这一维滚掉)。

2、使用压位技巧。仍然用二进制分组的做法,注意到 是由 转移过来的( 为个数, 为贡献),因此相当于将 这个布尔数组进行移位然后做按位或运算,使用压位技巧即可做到 ,其中 为字长(通常 )。

算法二

其实并没有必要把改掉多少个放到状态定义里面……设 表示从前 个段中选取至少几个数改成 才能使得 ,转移时枚举从第段取数的个数即可。最后一个可行当且仅当不超过初始数组中的个数。这样暴力地实现就是的时间、的空间,加上滚动数组就是的空间,加上二进制分组变成01背包就是的时间了。(这里滚动数组并不需要开第一维大小为的二维数组,直接在一个大小为的数组上DP即可,可以节省一定的代码量)

results matching ""

    No results matching ""