【问题标题】:Minimal non-contiguous sequence of exactly k elements恰好 k 个元素的最小非连续序列
【发布时间】:2016-11-23 05:47:43
【问题描述】:

我遇到的问题可以简化为:

给定一个由N 正数组成的数组,找出和最小的恰好是K 元素的非连续序列。

Ok-ish:只报告总和。奖励:可以识别选择的元素(至少一组索引,如果很多可以实现相同的总和)。

(通俗地说:从 N 个值中选择任意 K 个非相邻元素,使它们的总和最小)

当然,2*K <= N+1(否则无法解决),该问题对正/负不敏感(只需使用 MIN=min(A...) 移动数组值,然后将 K*MIN 添加回答案)。

到目前为止我得到了什么(天真的方法):

  1. 选择最接近最小值的值的K+2 索引。我不确定这一点,对于K=2,这似乎是涵盖所有特定情况所必需的,但我不知道对于K>2** 是否需要/足够
  2. 蛮力从上一步产生的索引值的最小总和尊重非连续性标准 - 如果我是对的并且K+2 就足够了,我可以强制使用(K+1)*(K+2) 解决方案空间但是,就像我说的。我不确定K+2 是否足以满足K>2 (如果事实上2*K 点是必要的,那么暴力破解就会超出窗口 - 二项式系数C(2*K, K) 增长得非常快)

关于如何以最小的时间/空间复杂度完成此任务的任何聪明想法?

** 对于K=2,这是一个重要的示例,其中需要 4 个最接近绝对最小值的值才能选择目标总和 [4,1,0,1,4,3,4] - 不能使用 0 值建立最小和,因为它违反了非连续性标准。

PS - 如果您想展示代码 sn-ps,C/C++ 和/或 Java 将不胜感激,但任何具有良好语法或伪代码的语言都可以使用 (我认为“良好的语法”不包括 Perl ,不是吗?)

【问题讨论】:

  • 我会改写如下:在一个数字数组中,找到 k 个不连续的最小元素(它们会产生最小总和的事实是找到那些元素)。
  • 你确定所有k个元素都必须是非相邻的吗?严格来说,非连续序列只是意味着任何元素之间至少存在一个间隙。
  • @IngoBürk 绝对确定。所以确保我提供了外行术语的解释,其中强调了 non-neighboring
  • @FDavidov “我会改写如下:在一个数字数组中,找到 k 个不连续的最小元素”也许是,也许不是 - 我不明白这是怎么回事推动了解决方案。你愿意详细说明吗?
  • 所以,你看不到... .Ok... 生成节点的排序列表(从最小到最大),其中每个条目都包含值和它在原始列表中的索引。根据定义,最小值为 IN。然后,开始检查第二个是否与第一个连续,如果不是,将其添加到节点列表中,否则移动到下一个,...您不需要计算不同集合的总和并找到一个产生最小值的。现在更清楚了吗?

标签: algorithm


【解决方案1】:

假设输入数字存储在数组 a[N]

通用方法是DP:f(n, k) = min(f(n-1, k), f(n-2, k-1)+a[n])

需要 O(N*K) 时间并且有 2 个选项:

  • 用于惰性回溯递归解 O(N*K) 空间
  • 前向循环的 O(K) 空间

在大K的特殊情况下还有另一种可能:

  • 使用递归回溯
  • 使用 map(n, map(k, pair(answer, list(answer indices)))) 代替 N*K 大小的辅助数组
  • 保存此答案的答案和索引列表
  • 如果 k>N/2 则立即返回 MAX_INT

这样,对于 K~=N/2,您的时间将少于 O(NK),类似于 O(Nlog(N))。对于小 K,它将增加到 O(N*log(N)Klog(K)),因此在一般方法或特殊情况算法之间做出决定很重要。

【讨论】:

  • 谢谢,我已经准备好工作了,看看我能从中得到什么。 对于小K-es,你有什么胆量,可以做一个预选扫描并只保留一个序列进入DP而没有风险吗?当然,这些值的堆积不会有助于他的结果。假设我选择一个 K 最低值 + 左/右邻(跟踪它们的索引位置),将它们作为 DP 的输入进行整理,一种“原始代表”......会有风险吗?我应该寻找超过K吗? (我现在正在寻找空间复杂性,我几乎可以肯定你的建议是合理的)。
  • 取最低的 K 值看起来很有希望,但如果 DP 的时间很关键,我会选择 2*K 的最低值+neightbors。但是如果你完全为了内存复杂性并且使用足够低的K,你可以直接通过正向循环的DP。在这种情况下,您只需要保留 n-2、n-1 个 K 大小的切片来形成 n 个切片(总内存将是 ~K*K,因为解决方案候选列表的长度最多可以是 K 个元素)。因此,您可以即时处理输入,甚至无需将所有输入值保存在内存中(仅对应列表中的解决方案候选)。
【解决方案2】:

应该有一种动态编程方法。

从左到右沿着数组工作。在每个点 i,对于 1..k 中的每个 j 值,找到从 1..i 中挑选 j 个非连续元素的正确答案的值。您可以通过查看 i-1、i-2 处的答案和 array[i] 的值来计算 i 处的答案。您想要的答案是长度为 n 的数组在 n 处的答案。完成此操作后,您应该能够通过沿数组回溯来确定元素是什么,以确定每个点的最佳决策是否涉及在该点选择数组元素,以及是否使用 array[i -1][k] 或数组[i-2][k-1]。

【讨论】:

  • 我不确定 DP over j in [1..K] 会有什么帮助。要应用 DP,如果我从下一个级别来看,子问题的答案一定不能改变。在我提供的示例中,子问题 j=1 会将目标置于索引 2(val 0)。如果我要添加另一个,prev 子问题将无济于事 - 要么我保留它并得到一个糟糕的结果,要么将它扔掉,我之前没有更好的棕褐色。或者......也许我没有很好地理解你的意思?
  • @AdrianColomitchi:我们正在尝试计算 f(i, j),这是在不仅使用位置
  • @j_random_hacker 好吧,你是对的。一开始有点困惑,因为在阅读文本解释时,我偶然发现了对 j=1..k 的依赖,并且完全错过了“i-1、i-2 的答案和数组 [i] 的值”部分。我必须承认,当亚历山大的回答进来时,我的第一反应是“但是……很明显,我怎么会错过呢?”
猜你喜欢
  • 2013-10-22
  • 1970-01-01
  • 2022-12-20
  • 1970-01-01
  • 2016-03-24
  • 2019-08-13
  • 1970-01-01
  • 2015-10-31
  • 2019-08-20
相关资源
最近更新 更多