【问题标题】:Can all objects be paired in a sequence?所有对象都可以按顺序配对吗?
【发布时间】:2019-02-28 06:09:23
【问题描述】:

首先,这不是作业或类似的东西,这是我上一个问题Candidate in finding majority element in an array的提示性问题。

有n种类的对象O1, O2, ..., On,并且有一个数组F[1...n],F[i]是Oi的个数(即有F[i]Ois,数组F[1...n]是给定的),每个F[i] > 0 .

现在使用以下规则来配对对象:

如果i != j,Oi可以和Oj配对,
else if i == j, Oi 不能与 Oj 配对。

也就是说,只有两种不同类型的对象可以相互配对。

  1. 输入数组F[1...n] 是否存在有效的配对方法?给出一个时间复杂度最好的算法来判断真假并证明其正确性。

  2. 如果存在,则输出一个有效的配对序列。给出你的算法和时间/空间复杂度分析。

例如, 输入 F[] = {10, 2, 4, 4};

那么至少存在一种有效的配对方式:

2 O1s 和 2 O2s 配对,4 O1s 和 4 O3 s 配对,4 O1s 和 4 O4s 配对。

一个有效的配对顺序是:

(1,2) (1,2) (1,3) (1,3) (1,3) (1,3) (1,4) (1,4) (1,4) (1 ,4)

【问题讨论】:

  • 您在比较索引 - 对吗?
  • 对不起,数组从哪里来的?
  • @KyleStrand 你不需要计算数组,数组是给定的。数组F[] 是算法的输入。
  • 我的意思是“数组与问题有什么关系”,但您的第一次编辑澄清了它。
  • @KyleStrand 我重新编辑了问题并给出了一个例子。

标签: arrays algorithm


【解决方案1】:

检查 O(n) 中是否存在解

让s 是F 的总和。

  • 如果s 是奇数,则没有解决方案(直观)
  • 如果存在i 使得F[i] > s/2 没有解决方案(直观)
  • 否则,存在解决方案(以下是构造证明)

在 O(n) 中找到解决方案

# Find s
s = 0
for i in 1..n:
    s += F[i]

# Find m such that F[m] is maximal
m = 1
for i in 1..n:
    if F[i] > F[m]:
         m = i

if s % 2 != 0 or F[m] > s/2:
    fail

a = 1
b = 1

# Pair off arbitrary objects (except those of type m) until F[m] = s/2    
while s/2 > F[m]:
    # Find a type with a non-zero frequency
    until F[a] > 0 and a != m:
        a = a + 1
    # Find another type with a non-zero frequency
    until F[b] > 0 and b != m and b != a:
        b = b + 1

    count = min(F[a], F[b], s/2 - F[m])
    pair(a, b, count)

# Pair off objects of type m with objects of different types
while F[m] > 0:
    # Find a type with a non-zero frequency
    until F[a] > 0 and a != m:
        a = a + 1
    pair(a, m, F[a])

end of algorithm

def pair(a, b, count):
    # Pairs off 'count' pairs of types a and b
    F[a] = F[a] - count
    F[b] = F[b] - count
    s = s - (2 * count)
    output "Pairing off $a and $b ($count times)"

两个while 循环都是线性的。第一个 while 循环在每次迭代时将 a 或 b 至少增加一个,因为在匹配 count 对之后,F[a] 为零,或 F[b] 为零,或 s/2 = F[m] 和循环终止。在访问所有元素之前,a 和 b 最多可以增加 n 次。第二个 while 循环也是线性的,因为它在每次迭代期间至少将 a 递增 1。

关键不变量是
(1) F[m]是F的最大元素
(2) F[m] <= s/2
我认为两者在检查时都相当明显。

对于内部循环,只要s/2 > F[m] 必须至少有两个非零频率的其他对象类型。如果只有一个,比如说a,那么
F[a] + F[m] = s
F[a] = s - F[m] > s - s/2(来自循环条件)
F[a] > s/2
F[a] > F[m]
这是不变量 (1) 的矛盾。

由于至少有两种类型具有非零频率(除了m),因此循环将能够找到类型a 和b 并配对对象直到s/2 = F[m]。

第二个循环很简单。由于恰好有一半的对象是 m 类型的,因此每个 m 类型的对象都可以与不同类型的对象配对。

【讨论】:

  • 这是一个您的算法失败的示例:项目 p,q,r,s 在 F 中按该顺序列出的频率为 7,6,3,2。“配对任意对象”首先对 q & r with count=3,将 F[1], F[2] 减少到 3, 0。然后 s=12,因此“配对 m 类型的对象”循环运行并失败。
  • @jwpat7 它只会配对 q & r 2 次,因为 min(F[a], F[b], s/2 - F[m]) = min(6, 3, 18/2 - 7) = 2
  • 好的 - 我忽略了 s/2 - F[m] 术语
  • @Tom:感谢您提供了一个反例,该示例在我的最大流算法的推理中引发了一个关键缺陷(简而言之,它缺乏足够数量的约束来正确限制个体的出现成对的对象)。似乎无法(容易地)弥补这一缺陷。因此我撤回了我的回答。对不起所有被带到花园小路上的人。
【解决方案2】:

这里有一个建议。不过,我不确定它是否适用于所有可能的情况,或者它是否是最有效的算法。

设n 为索引总数。构造对象类型数量的最高优先级队列,其中每个对象类型为其索引i。换句话说,创建一个优先级队列,其中队列中的排序值是F 的值。将每个节点与该类型的所有对象的列表相关联。这将花费O(n log(n)) 时间。

对于每个对象类型,从具有最多重复项的类型开始,然后到具有最少重复项的类型,将对象“类”中的一个对象与仍然有剩余对象的每个其他类的一个对象配对在它们中,并从队列中的该节点中删除该对象。由于除了顶部的每个队列项都将少一个对象,因此大多数队列仍将按优先级队列顺序排列;然而,顶部节点将有n-1 更少的项目(或者它将是空的),因此向下堆以维护队列。此外,删除没有剩余对象的节点。使用新的最高队列值重复此过程,直到所有节点都配对。这将花费O(n log(n) + k) 时间,其中k 是项目总数。假设k明显大于n,则总时间复杂度为O(k)。

同样,我不太确定这是否总能找到解决方案,如果可能的话。我的直觉是,如果您在每次配对后重新堆放(如有必要),您将确保如果可以进行完整配对,则 将 被发现,但 (1) 这会效率低得多,以及(2)我不确定它会在哪些情况下成功而原始算法不会成功,以及(3)即使每次都能奏效,我也不完全确定。

至于F的哪些值无解,显然如果存在一类对象的元素比所有其他类的总和还多,则不可能配对。除此之外......我不太确定。调查我的“改进”算法是否正确评估每个案例会很有趣。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-08
    • 2014-07-08
    相关资源
    最近更新 更多