目前我正在阅读Skiena的“算法设计手册”(好了,开始阅读)
他问一个他称之为“电影调度问题”的问题:
问题:电影调度问题
输入:线上的n个区间的集合I.
输出:相互非重叠区间的最大子集是多少 从我选择?
示例:(每条虚线都是一部电影,你想找到一部电影数量最多的一套)
----a---
-----b---- -----c--- ---d---
-----e--- -------f---
--g-- --h--
我想要解决它的算法是这样的: 我可以扔出“最坏的罪犯”(与其他大多数电影相交),直到没有最严重的罪犯(零交叉点)。我看到的唯一问题是,如果有一个平局(比如两部不同的电影,每部电影与其他3部电影相交),我扔出哪一部电影是否重要?
基本上我想知道如何将这个想法变成“数学”以及如何证明它是正确的/不正确的。
答案 0 :(得分:11)
算法不正确。让我们考虑以下示例:
|----F----| |-----G------|
|-------D-------| |--------E--------|
|-----A------| |------B------| |------C-------|
您可以看到大小至少为3的解决方案,因为您可以pick A, B and C
。
首先,让我们计算每个区间的交叉点数量:
A = 2 [F, D]
B = 4 [D, F, E, G]
C = 2 [E, G]
D = 3 [A, B, F]
E = 3 [B, C, G]
F = 3 [A, B, D]
G = 3 [B, C, E]
现在考虑一下你的算法。在第一步中,我们删除B
,因为它与最多的invervals相交,我们得到:
|----F----| |-----G------|
|-------D-------| |--------E--------|
|-----A------| |------C-------|
很容易看出,现在从{A, D, F}
你只能选择一个,因为每对都相交。与{G, E, C}
相同的情况,因此在删除B
后,您最多只能选择一个来自{A, D, F}
,最多一个来自{G, E, C}
,以获得{{1}的总数},小于2
的大小。
结论是,在删除与最多次数相交的{A, B, C}
后,您无法获得最大数量的非相交电影。
问题是众所周知的,一种解决方案是选择首先结束的间隔,删除与其相交的所有间隔并继续直到没有要检查的间隔。这是一个贪婪方法的例子,你可以找到或开发一个正确的证据。
答案 1 :(得分:2)
这对我来说似乎是一个dynamic programming问题:
定义以下功能:
sched(t) = best schedule starting at time t
next(t) = set of movies that start next after time t
len(m) = length of movie m
next
会返回一个集合,因为可能会有多个影片同时启动。
然后sched
应定义如下:
sched(t) = max { 1 + sched(t + len(m)), sched(t+1) } where m in next(t)
此递归函数从m
中选择一部电影next(t)
,并比较包含或不包含m
的最大可能集。
使用第一部电影的时间调用sched
,您将获得最佳设置的大小。获得最佳设置本身只需要一些额外的逻辑来记住您在每次调用时选择的电影。
我认为如果你使用memoization,这个递归(而不是迭代)算法在O(n ^ 2)中运行,其中n是电影的数量。
这是正确的,但是我必须参考我的算法教科书给你一个明确的证明,但希望这个算法直观地理解为什么它是正确的。
答案 2 :(得分:0)
# go through the database and create a 2-D matrix indexed a..h by a..h. Set each
# element of the matrix to 1 if the row index movie overlaps the column index movie.
mtx = []
for i in range(8):
column = []
for j in range(8):
column.append(0)
mtx.append(column)
# b <> e
mtx[1][4] = 1
mtx[4][1] = 1
# e <> g
mtx[4][6] = 1
mtx[6][4] = 1
# e <> c
mtx[4][2] = 1
mtx[2][4] = 1
# c <> a
mtx[2][0] = 1
mtx[0][2] = 1
# c <> f
mtx[2][5] = 1
mtx[5][2] = 1
# c <> g
mtx[2][6] = 1
mtx[6][2] = 1
# c <> h
mtx[2][7] = 1
mtx[7][2] = 1
# d <> f
mtx[3][5] = 1
mtx[5][3] = 1
# a <> f
mtx[0][5] = 1
mtx[5][0] = 1
# a <> d
mtx[0][3] = 1
mtx[3][0] = 1
# a <> h
mtx[0][7] = 1
mtx[7][0] = 1
# g <> e
mtx[4][7] = 1
mtx[7][4] = 1
# print out contstraints
for line in mtx:
print line
# keep track of which movies are still allowed
allowed = set(range(8))
# loop through in greedy fashion, picking movie that throws out the least
# number of other movies at each step
best = 8
while best > 0:
best_col = None
best_lost = set()
best = 8 # score if move does not overlap with any other
# each step, only try movies still allowed
for col in allowed:
lost = set()
for row in range(8):
# keep track of other movies eliminated by this selection
if mtx[row][col] == 1:
lost.add(row)
# this was the best of all the allowed choices so far
if len(lost) < best:
best_col = col
best_lost = lost
best = len(lost)
# there was a valid selection, process
if best_col > 0:
print 'watch movie: ', str(unichr(best_col+ord('a')))
for row in best_lost:
# now eliminate the other movies you can't now watch
if row in allowed:
print 'throwing out: ', str(unichr(row+ord('a')))
allowed.remove(row)
# also throw out this movie from the allowed list (can't watch twice)
allowed.remove(best_col)
# this is just a greedy algorithm, not guaranteed optimal!
# you could also iterate through all possible combinations of movies
# and simply eliminate all illegal possibilities (brute force search)