集合选数最值一类问题

一共有两种类型,我分别介绍。

类型一

先来看一道简单的题目:

POJ2442 Sequence

给你\(m\)个序列,每个序列有\(n\)个非负整数,你现在要在每个序列选一个数,这一共有\(n^m\)种方案,一种方案的值定义为所选的数的和,要你输出值最小的\(n\)种方案的和。

数据范围: \(0 \lt m \le 100\), \(0 \lt n \le 2000\)。

先考虑\(m = 2\)的情况,一共有\(n ^ 2\)种方案,设两个序列为\(a, b\),假设我们已经把它们排好序了,即\(a_i \le a_{i+1} \ (1 \le i \lt n)\), \(b_i \le b_{i+1} \ (1 \le i \lt n)\)。方案\(f(i, j)\)的值为\(a_i + b_j\)。\(f\)有以下性质:\(f(i, j) \le f(i, k) \ (j \lt k)\),\(f(i, j) \ (1 \lt j \le n)\)如果是最后的答案,那么\(f(i, j - 1)\)肯定也是答案。我们只需要维护一个优先队列,初始时将\(f(i, 1)\)加入优先队列,从优先队列中第\(k\)次取出的\(f(i, j)\)即为第\(k\)小的方案,每次取出后,若\(j \lt n\),把\(f(i, j + 1)\)加入优先队列。时间复杂度为\(O(nlog_2{m})\)

至此,我们有了\(m = 2\)的合并算法,当\(m \gt 2\)时,我们只需要将\(m\)个序列进行\(m - 1\)次合并。得到的即为答案。总的时间复杂度为\(O(mnlog_{2}m)\),可以通过此题。

我们考虑与此题类似的一个问题,给定\(n\)个多重集合\(c_i\),第\(i\)个集合的大小为\(s_i\),要在每个集合中选一个数,一种方案的值定义为所选的数的和(或积),求第\(k\)大(或小)的方案的值。

对于此问题上面这个做法还不够优秀。

以下介绍第一种更为优秀的做法。

以求第\(k\)大为例。

先把集合内元素排序,再以集合的最大值与次大值的差作为关键字对集合进行排序,即对于集合\(i\)满足\(s_{i, j} \le s_{i, j + 1}, \ s_{i, 1} - s_{i, 2} \le s_{i + 1, 1} - s_{i + 1, 2}\) (如果某个集合只有一个元素,那么这个集合只有一种选法,就可以不用考虑了)。

把上面\(f\)的维数扩展到\(n\)维,\(f(p_1, p_2, \cdots , p_n)\)代表一个方案,第\(i\)个集合选了第\(p_i\)大的数。每个集合默认选择最大的数,这种选择作为初始方案,再进行逐个集合更改所选的数。我们只需要记录当前待选择的集合的编号\(i\),当前集合所选的数第\(j\)大的数,以及当前方案的值。之前的选择\(p_1, p_2, \cdots p_{i - 1}\),完全可以丢弃,\(p_{i + 1}, p_{i + 2}, \cdots p_n\)默认都是选择最大的,也不用记录,所以\(f\)只有三维\((i, j, sum)\)。

对\(f(i, j, sum)\)的后继进行定义:

若\(j \lt c_i\),\(f(i, j + 1, sum - s_{i, j} + s_{i, j + 1})\)是它的后继;

若\(i \lt n\),\(f(i + 1, 2, sum - s_{i + 1, 1} + s_{i + 1, 2})\)是它的后继;

若\(j = 2 \ \&\& \ i \lt n\),\(f(i + 1, 2, sum + s_{i, 1} - s_{i, 2} - s_{i + 1, 1} + s_{i + 1, 2})\)是它的后继;

相应的,我们定义前驱。

我们把每种方案看成一个节点,它与它的后继之间的边为后继边,它与它的前驱之间的边为前驱边。

存在以下性质:

\(f(i, j, sum)\)不小于它的后继,不大于它的前驱。

因为前面我们对集合进行了排序,

满足\(s_{i, j} \ge s_{i, j + 1}\),所以第一种和第二种后继(如果存在)存在这种性质。

满足\(s_{i, 1} - s_{i, 2} \le s_{i + 1, 1} - s_{i + 1, 2}\),所以第三种后继(如果存在)存在这种性质。

\(f(i, j, sum)\)可以有多个后继,但只有唯一的前驱。

因为我们是逐位更改的,当前集合和所选的数的不同,这两种方案通过后继边所能达到的方案不会有交。

对于任意合法的方案最终一定能通过前驱边到达初始方案。

即从最初方案通过后继边可以到达任意合法方案。

这很显然。

那么这就是一棵树。如果一种方案成为答案,那么它的前驱肯定也是答案。那么我们可以维护一个优先队列,存当前可能成为答案的方案,第\(k\)个答案即为优先队列第\(k\)次的最大值,一种方案出队后把它的所有后继加入优先队列。

事实上,这种结构只要是一个\(DAG\),就可以用优先队列维护了,只不过可能要去重。

这个算法的时间复杂度为\(O(nlog_{2}n + \sum_i^n{c_ilog_{2}c_i}+ klog_{2}mk)\),其中\(m\)为每种方案的平均后继个数。由于\(m\)是常数,可以忽略,那么时间复杂度就是\(O(nlog_{2}n + \sum_i^n{c_ilog_{2}c_i}+ klog_{2}k)\)。这个算法是相当优秀的。

模板题LibreOj #6254. 最优卡组

类型二

假设我们始终在一个集合内选数,即给你一个有\(n\)个元素的多重集合\(s\),选\(m\)个不同的数定义为一种方案,求第\(k\)大(或小)的方案的值。

利用上面的算法的思想,\(f(p_1, p_2, \cdots , p_m) \ (1 \le p_1 \lt p_2 \lt \cdots \lt p_m \le n)\)为一种方案。

定义后继:如果\(f(p_1, p_2, \cdots , p_i + 1, \cdots , p_m)\)合法,那么它为\(f(p_1, p_2, \cdots , p_m)\)的后继。

类似的定义前驱。

但是这是一个DAG,如果这样直接用优先队列做,需要去重。

我们再次利用逐位更改的思想,多加一维\(i\),表示\(p_{i + 1}, \cdots , p_m\)不会更改。

\(f(i, p_1, p_2, \cdots , p_i, \cdots , p_m)\)的后继有:

如果\(f(i, p_1, p_2, \cdots , p_i + 1, \cdots , p_m)\)合法,那么它为\(f(i, p_1, p_2, \cdots , p_m)\)的后继。

如果\(f(j, p_1, p_2, \cdots , p_j + 1, \cdots , p_m) \ (1 \le j \lt i)\),那么它为\(f(i, p_1, p_2, \cdots , p_m)\)的后继。

显然它也有上面的三个性质。

那么这就是一颗树了,可以直接用优先队列做,不需要去重了。

Sgu 421. k-th Product

给出\(n\)个整数\(a_1, a_2, \cdots , a_n\),问从中选\(m\)个数乘积第\(k\)大是多少。

数据范围:\(1 \le n,k \le 10000\), \(1 \le m \le13\), \(k \le C^n_m\), \(-10^6 \le a_i \le10^6\)。

假如求的是数和的第\(k\)大,或正整数的乘积第\(k\)大是多少,那么可以直接用上面的那个算法。

但是所选的负数的个数或影响乘积的正负,所以我们枚举\(m\)中选的负数的个数。如果负数个数为偶数,这类方案的乘积非负,选择求绝对值前\(k\)大的乘积。反之,这类方案的乘积非正,选择求绝对值前\(k\)小的乘积。

参考资料

POJ2442 Sequence

第十三届北航程序设计竞赛预赛

第十三届北航程序设计竞赛预赛题解

LibreOj #6254. 最优卡组

Sgu 421. k-th Product

bzoj 1425: SGU 421 k-th Product

Sengxian‘s Blog

ZJOI2010讲课

原文地址:https://www.cnblogs.com/tkandi/p/9375509.html

时间: 2024-10-08 16:42:42

集合选数最值一类问题的相关文章

bzoj2734【HNOI2012】集合选数

2734: [HNOI2012]集合选数 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 831  Solved: 487 [Submit][Status][Discuss] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数

BZOJ 2734 集合选数(状态压缩DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2734 题意:给出一个由1到n的数字组成的集合.定义合法子集为若x在子集中则2x.3x均不能在子集中.求有多少个合法的子集. 思路: 1   3    9 2   6    12 4   12   36 对于上面的矩阵,我们发现就等价于不选相邻数字的方案数.因此枚举每个还没有用到的数字,建立以该数字为左上角的矩阵.接着就是状态压缩DP. int a[N][N]; i64 f[2][1<<

【BZOJ-2732】集合选数 状压DP (思路题)

2734: [HNOI2012]集合选数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1070  Solved: 623[Submit][Status][Discuss] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n

[HNOI2012][BZOJ2734] 集合选数|状态压缩动态规划|思路题

2734: [HNOI2012]集合选数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 708  Solved: 414[Submit][Status][Discuss] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤

bzoj 2734: [HNOI2012]集合选数 状压DP

2734: [HNOI2012]集合选数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 560  Solved: 321[Submit][Status] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何

[HNOI2012]集合选数 --- 状压DP

[HNOI2012]集合选数 题目描述 <集合论与图论>这门课程有一道作业题,要求同学们求出\({1,2,3,4,5}\)的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中. 同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数, 如何求出\({1,2,3...n}\) 的满足上述约束条件的子集的个数(只需输出对 \(10^{9}+1\) 取模的结果),现在这个问题就交给你了. 输入格式: 只有一行,其中有一个正整数 \(n\) 30

bzoj 2734: [HNOI2012]集合选数

Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 779  Solved: 456[Submit][Status][Discuss] Description <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,...,

[BZOJ2734][HNOI2012]集合选数

试题描述 <集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中.同学们不喜欢这种具有枚举性质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就交给你了. 输入 只有一行,其中有一个正整数 n,30%的数据满足 n≤20. 输出 仅包

BZOJ 2734 集合选数

高妙的算法-- 可以构造出形如: 1  2  4   8   16  32 64 3  6  12  24 48 9  18 36 27 54 的矩阵 相邻的数不能被同时选到 因此 将每一个数构造进矩阵 然后状态压缩dp 根据乘法原理 就可以 得出所有的方案 1 #include <bits/stdc++.h> 2 #define mod 1000000001 3 using namespace std; 4 int n,vis[100010],ans,num[23]; 5 int mps[2