检查 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]
。
第二个循环是微不足道的。由于恰好有一半的对象是 type m
,因此每个 type 对象m
都可以与不同类型的对象配对。