「小组联考」最优卡组

题目

【内存限制:512 MiB】【时间限制:1000 ms】
【标准输入输出】【题目类型:传统】【评测方式:文本比较】

题目描述

$chitanda$ 有 $k$ 个卡包,第 $i$ 个卡包里有 $c_i$ 张卡,每张卡有一个能力值,其中第 $i$ 个卡包里的第 $j$ 张卡具有 $a_{i, j}$ 点能力值。

他准备选择 $k$ 张卡牌的组合,其中每个卡包要选择恰好一张卡牌。他希望这 $k$ 张卡牌的能力值之和尽量大,请你告诉他在所有可能的组合里,能力值之和最大的 $n$ 个组合分别具有多大的能力值。

输入格式

第一行包含两个整数 $k$ 和 $n$,含义见题目描述。

接下来 $k$ 行,每行描述一个卡包的信息,其中第 $i$ 行的第一个整数表示 $c_i$,接下来该行会有 $c_i$ 个整数,依次表示这个卡包里每张卡的能力值。

输出格式

输出一行,包含 $n$ 个整数,相邻两个数字之间用空格隔开,其中第 $i$ 个整数表示第 $i$ 大的能力值之和。

样例

样例输入
2 9
3 1 3 5
3 2 3 3
样例输出
8 8 7 6 6 5 4 4 3

样例解释

$chitanda$ 有 $3 \times 3$ 种选法,能力值分别为 $1 + 2, 1 + 3, 1 + 3, 3 + 2, 3 + 3, 3 + 3, 5 + 2, 5 + 3, 5 + 3$。

数据范围与提示

对于所有数据,$k \geq 2,$ $1 \leq n \leq \prod\limits_{i = 1}^{k}{c_i},$ $c_i \geq 1,$ $1 \leq a_{i, j} \leq 10^9$ $(i = 1, 2, \cdots, k; j = 1, 2, \cdots, c_i)$。

题解

做题经历

考试的时候想到一个很类似于正解的东西,预估时间复杂度 $O(nk)$ ,但是想到很多限制,结果代码都不敢码......

最后痛失 $50pts$ 。

接下来说一下这个思路吧。

$SUBSULOTION#50pts$

首先,我们先将每个卡组内的卡从大到小排个序。

然后,我们再定义一个数字串,定义不方便说,直接拿例子解释

有这样一个数字串:

数位: $12345$

数串: $13211$

这个数串表示的意思就是:在这种情况下,第一组取的是第一大的,第二组取的是第三大的,第三组取的是第二大的......以此类推

那么,毫无疑问,最大的能力值一定是 $11111$

然后呢?次大的呢?

我们不知道次大的是哪个,但是我们肯定可以保证次大在 $21111、12111、11211、11121、11112$ 中

我们把这些数串叫做子状态

那么这个思路就出来了:

把状态放进优先队列里面,每次取出最大的状态,输出,再放进它的子状态......重复 $n$ 次即可。

但是这样呢,每个状态会最多被放入 $k$ 次,为什么?

举个栗子:

状态 $23426$

它可能被 $13426、22426、23326、23416、23425$ 放进去。

那么时间复杂度大概是 $O(n\cdot k\cdot log)$

看看范围,还可以过一半,挺好......

正解

一道思路诡异的贪心题,但是这个思路也不是不可想,只是要分析到点上。

我们其实可以在 $50pts$ 的思路上加以改进。

首先,超时思路为什么会时间复杂度高?

是因为同一个状态被最多放进了 $k$ 次。


那么怎么解决这个问题呢?

这里有一个最终的解决方法,先说出来,然后解释其正确性以及如何思考。

首先,我们可以在输入的时候得到 $k$ 个串 $a_{i,j}$ 表示第 $i$ 组卡牌的能力值

将每个组内卡牌从大到小排序,然后设 $val_i=a_{i,1}-a_{i,2}$

具体意思就是 $val_i$ 表示第 $i$ 组中最大的牌减次大的牌的值

再按照 $val_i$ 从大到小把数组顺序重排

在处理完这些之后,我们来看怎么放进状态的子状态

设我们有一个状态 $\overline{×××...××××ab11111......11111}$

其中 $×$ 不用管,就是来占位置用的,而 $b$ 之前全是 $1$

那么我们会对这个状态做出三个转移,其中一个是特例:

  • 子状态一:$\overline{×××...××××a(b+1)11111......11111}$
  • 子状态二:$\overline{×××...××××ab21111......11111}$
  • 子状态三(仅当 $b=2$ 时):$\overline{×××...××××a(b-1)21111......11111}=\overline{×××...××××a121111......11111}$

最后把他们放入优先队列,同样地维护即可。

那么这样做有什么好处吗?

首先,它不会出现重复状态被压入优先队列。

为什么?

假设队列中有这样一个状态:$\overline{×××...××××ab11111......11111}$(注意不要和之前的状态混淆了)

如果 $b≥2$ 且当 $b=2$ 时 $a≠1$ ,那么它只有一个前驱 $\overline{×××...××××a(b-1)11111......11111}=\overline{×××...××××a111111......11111}$

但是如果 $b=2$ 且 $a=1$ 时,我们可以否决其前驱是 $\overline{×××...××××a(b-1)11111......11111}=\overline{×××...××××1111111......11111}$ ,这个很好想

那么它的祖先就有且只有 $\overline{×××...××××1211111......11111}$ ,就是那种特殊子状态

所以说,这样转移显然每种状态就只会出现一次

那么,它还有什么优势呢?

又来举同一个栗子,我们有状态 $\overline{×××...××××ab11111......11111}$

那么,当不是特殊情况时,我们的转移是 $\overline{×××...××××(a+1)b11111......11111}$ 或者是 $\overline{×××...××××a(b+1)11111......11111}$

那么,这两种状态。是否都比 $\overline{×××...××××(a+1)b11111......11111}$ 小呢?答案是肯定的。

但如果是特殊情况:$\overline{×××...××××2111111......11111}\Rightarrow \overline{×××...××××1211111......11111}$

这样转移的话,子状态是否也是比前驱状态小呢?

答案也是肯定的。

为什么?提示:这和我们开始时那个特别的排序也是有关的。

综上,这个处理方案有两点好处:

  • 状态唯一
  • 子状态大小有序

那么我们看一看,其时间复杂度是不会超过 $O(3nlog)$ 的。


但是又有一个问题:这个神仙思路是怎么想到的呢?
首先,对于 $50pts$ 的思路是一定要想到的,然后对于时间复杂度进行分析。
时间复杂度 $O(knlog)$ ,首先这个 $n$ 是一定跑不掉的,然后 $log$ 也肯定是有的,因为我们始终要用堆进行维护。
那么我们就要尽可能地将 $k$ 变小。
这个 $k$ 是跟子状态有关的,那么我们要尽可能减小子状态的数量。
那么,我们引入一个比较好想的思路:每次只更改第一个不是 $1$ 的位置
比如对于一个状态:$\overline{×××...××××ab11111......11111}$
那么它的子状态就是 $\overline{×××...××××a(b+1)11111......11111}$ 或者 $\overline{×××...××××ab21111......11111}$
但是有一种情况:$\overline{211...112}$,在我们的子状态定义中是够不到的。
所以还要再定义一个子状态:$\overline{×××...××××a(b-1)(1+1)111......11111}$,再普遍一点:$\overline{×××...××××(a-1)(b+1)1111......11111}$
这样我们就可以从$211..1$ 到 $1211...11$ 到 $1121...11$ ....到 $1111...2$,把这种特殊情况包含进去
但是这样又会有一个问题了,举个栗子,状态 $232$ 可以通过状态 $241$ 得到,也可以通过 $231$ 得到,这样就重复了。
所以为了避免重复,我们只在 $a=1,b=2$ 时进行最后一次子状态下传。
而将 $a=1,b=2$ 转成 $a=2,b=1$ 的同时,又要保证先大后小,就加入了那个奇奇怪怪的排序方式。

$AC$ 思路就是这样,代码:
题解都打了那么多,代码却写不出来了......

猜你喜欢

转载自www.cnblogs.com/MachineryCountry/p/11728008.html