1 条题解
-
0
E. Alex and Complicated Task 详细题解
题目重述
给定一个长度为 的序列 。要找一个最长的子序列 (不要求连续),使得 的长度为 ( 为整数),并且对于每个 (从 开始)满足:
换言之, 可以划分为若干个长度为 的块,每个块形如 (即两个数重复两次)。求最长可能的 并输出任意一个。
观察与转化
条件 且 等价于每个长度为 的块由两个数 和 组成,形式为 。因此,我们需要在原序列 中找到一个最长子序列,它能够被分割成若干这样的四元组,且这些四元组在子序列中按顺序出现。
由于子序列保持原顺序,我们可以把问题看作:在原序列中,寻找尽可能多的 不重叠的 形如 的模式,其中第一个 出现在第一个 之前,第二个 出现在第二个 之前,并且这些四元组按顺序排列(即每个四元组的四个元素在原序列中的下标递增,且不同四元组之间的下标也递增)。
关键简化:相邻数对
注意到一个四元组 可以视为 两个相同的相邻数对 按顺序出现。即如果我们把原序列中所有相邻元素对 看作一个单位,那么一个四元组 对应于两个相邻数对 ,它们在原序列中可以不连续(只要第一个数对的第二个元素在第二个数对的第一个元素之前即可)。但是,子序列的选取允许跳过元素,因此如果我们能够找到两个位置 ,使得 且 ,那么我们就可以取出 作为子序列,正好构成 。这里要求 以保证顺序(但即使 ,即两个数对相邻,也是允许的,因为子序列可以取 连续四个元素)。
因此,问题转化为:在序列的相邻元素对中,找出尽可能多的 相同的无序对(这里是有序对,因为 与 不同),每个对使用两次,且这些对的使用顺序必须与它们在原序列中出现的顺序一致,且不能重叠(即同一个数组元素不能同时属于两个不同的数对)。实际上,由于我们取的是子序列,每个元素可以被多个数对共享吗?注意:如果我们取第一个数对用了 ,第二个数对用了 ,为了保证子序列的顺序,必须有 (或者 且 ,但 不能与 冲突,因为 是同一个值也没关系,只要下标不同,子序列允许重复值。然而,如果 ,那么 同时作为第一个数对的第二个元素和第二个数对的第一个元素,在子序列中同一个位置只能被用一次,因此 时,我们实际上需要取 ?不,我们需要四个元素 ,当 时,这四个元素就是 ,这仍然是合法的,因为 出现了两次(一个作为第一个数对的末尾,一个作为第二个数对的开头),但在子序列中下标 只能出现一次。所以不能重复使用同一个下标。因此我们必须要求 ,即两个数对之间至少间隔一个位置(不共用元素)。这个问题在贪心处理时需要注意。
贪心算法
一种高效的解法如下(见参考代码):
- 从左到右扫描原序列,维护一个哈希表(或字典)记录 相邻数对 是否已经出现过。
- 对于每个位置 (),考虑数对 。
- 如果该数对已经在哈希表中标记为“出现过一次”,那么我们就找到一个匹配:之前已经有一个相同的 出现,且那个数对与当前数对不重叠(因为我们每次找到匹配后就删除标记,并且保证中间有间隔)。然后我们输出这四个数:(即先前的 和当前的 ),并将该数对的标记清除(表示已使用)。
- 否则,将该数对的标记设为“出现过一次”,继续扫描。
为什么这样能保证不重叠?因为一旦我们使用了一个数对(作为四元组的第一个数对),我们立刻将其标记删除,后续不会再使用它。而当前数对是刚扫描到的,它不可能与之前已使用的数对重叠,因为之前已使用的数对在更早的位置,且我们要求 是不断递增的,所以当前数对一定在已使用的数对之后,并且由于我们没有重复使用元素(每次输出四个元素时,我们实际上从原序列中取出了四个位置:假设之前匹配的数对在位置 ,当前数对在位置 ,那么取出的四个元素下标为 ,由于 (因为在 处匹配后,我们继续扫描 ,中间跳过了一些元素),因此下标不重叠。但严格来说,如果两个数对刚好相邻(?实际上 和 的关系:之前匹配的数对是 ?需要仔细定义索引。但无论如何,贪心算法在实现时只是简单地顺序扫描,并不显式检查下标是否重叠,然而由于每次匹配后立即删除该数对的记录,并且扫描指针继续前进,自然保证了不会重复使用同一个元素。一个潜在的覆盖问题:如果出现 连续出现三次,比如序列 ,那么第一次遇到第一个 时标记,第二次遇到第二个 时匹配并输出前四个元素 ,然后清除标记。第三次遇到第三个 时,由于标记已清除,它会重新标记,不会与前面冲突。这样我们得到了一组四元组 ,而剩下的两个元素 无法配对。这是最优的吗?实际上,最优解应该是取两个四元组:第一个 用位置1-4,第二个 用位置3-6?但位置3和4已经被第一个四元组使用了,不能重复。所以只能取一个四元组。贪心得到的结果就是最优的。
正确性证明
引理:将问题转化为选取不相交的相邻数对 ,每个数对最多被使用两次(且两次使用必须构成四元组),目标是最大化四元组数量。贪心算法每次遇到一个与之前未配对的相同数对时,立即将其配对,不会影响后续的配对机会。这是因为:
- 如果当前数对 与之前某个未配对的 相同,那么配对 和 是最优的,因为 是 之后第一个能与 配对的数对。若我们放弃 而等待后面更远的相同数对 ,则 可能会与更后面的某个数对 配对,但这样 和 之间的间隔更小,可能不会影响总数,但贪心选择 和 与选择 和 相比,前者占用更少的后续空间,不会减少可能的配对总数。更严格地,这是一个区间调度问题的变种:每个数对可以看作一个区间(它的两个下标),要求选取尽可能多的不相交区间对(每个对由两个相同数对组成),贪心取最早结束的匹配是经典的求最大匹配方法。事实上,算法等价于:扫描时,每当遇到一个与之前某个数对相同的数对,就立即将其匹配,这相当于按数对的出现顺序进行匹配,类似于括号匹配。由于每个数对必须使用两个相同实例,且每个实例只能使用一次,贪心匹配能获得最大数量的完整对(类似于找最多不重叠的相同元素对)。因此,该贪心算法能得到最优解。
算法步骤
- 初始化一个空的哈希表(或字典),用于记录每个相邻数对 当前是否已出现且未匹配。
- 初始化结果序列 为空。
- 遍历 从 到 (即相邻数对的下标为 ):
- 记当前数对 。
- 如果 为真(即之前出现过一次且未匹配),则:
- 将 依次加入 。
- 将 置为假(清除标记)。
- 否则:
- 将 置为真(标记出现一次)。
- 输出 以及 中的元素。
注意: 可以用
map或unordered_map实现,键为pair<int,int>。时间复杂度
- 遍历一次数组,每个数对至多两次操作(插入和查询),哈希表操作平均 ,总复杂度 。
- 空间复杂度 用于存储哈希表,但实际最多存储不同数对的数量,不超过 实际上 ,可行。
输出格式
第一行输出 ,即 的长度;第二行输出 中的每个整数,用空格分隔。
示例验证
例1:
- ,数对 ,未出现过,标记。
- ,数对 ,未出现过,标记(注意 与 不同)。
- ,数对 ,已标记,匹配,输出 。长度 。
例2: 扫描过程:
- 标记
- 标记
- 标记
- 已标记 -> 输出 ,清除
- 标记
- 标记
- 标记
- 再次出现 -> 输出 ,清除 最终得到 ,长度 。
符合样例输出。
总结
本题的核心是将四元组 转化为两个相同的相邻数对 ,然后利用贪心匹配(即每当出现第二次相同的数对时,立即输出一个四元组)来寻找最长子序列。这种贪心策略基于不重叠匹配的思想,保证得到最优解。该解法简单高效,时间复杂度 ,适合 的数据范围。
- 1
信息
- ID
- 6910
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者