贪心算法实例(一)求解活动安排问题
问题
活动选择问题是一个调度竞争共享资源的多个活动的问题。
设有n个活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源(如,演讲会场),而在同一时间内只有一个活动能使用这一资源。活动安排问题就是要在所给的活动集合中选出最大的相容活动子集合。
- 每个活动i都有一个要求使用该资源的开始时间si和一个结束时间fi,且si<fi。如果选择了活动i,则它在半开时间区间[si, fi)内占用资源。
- 若区间[si, fi)与区间[sj, fj)不相交,则称活动i与活动j是相容的 —— 当si≥fj或sj≥fi时,活动i与活动j相容。
思路
贪心算法只需要考虑一个选择,即“贪心”的选择。
- 将活动按照结束时间进行从小到大排序。
- 用i代表第i个活动:s[i]表示第i个活动开始时间,f[i]代表第i个活动的结束时间。
按照结束时间从小到大排序,挑选出结束时间早的活动,并且满足后一个活动的起始时间晚于前一个活动的结束时间(s[i+1]>f[i]),找出全部这些活动就是最大的相容活动子集合 —— 系统一次检查活动i是否与当前已选择的所有活动相容。若相容活动i加入已选择活动的集合中,否则,不选择活动i,而继续下一活动与集合A中活动的相容性。若活动i与之相容,则i成为最近加入集合A的活动,并取代活动j的位置。
参考:算法笔记(0002) - 【贪心算法】活动安排问题_YINUXY的博客-CSDN博客
实现
def activity_selector_greedy(s, f): """ :param s: starting with [0, ...] :param f: starting with [0, ...] :return: list of activity i """ n = len(s) k = 0 # record current activity id. res = [k] for i in range(1, n): if s[i] >= f[k]: res.append(i) print(f'a{i}: s{s[i]} -> f{f[i]}') k = i return res # s = [0, 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12] # f = [0, 4, 5, 6, 7, 9, 9, 10, 11, 12, 14, 16] # a1: s1 -> f4 # a4: s5 -> f7 # a8: s8 -> f11 # a11: s12 -> f16 # [0, 1, 4, 8, 11]
如果s[1]的值变成10,f[1]的值变成14,即活动1的开始时间是10,结束时间是14,则结果变成了[0, 1] —— 这个结果感觉是不对的。
所以,我自己根据这个问题本身,更改了一个版本:
def activity_selector_greedy1(s, f): """ :param s: starting with [0, ...] :param f: starting with [0, ...] :return: list of activity i """ max_time = 999999 n = len(s) c = 0 # record current activity id. m = 0 # record minimum finish time res = [0] while f[c] < max_time and m < max_time: m = max_time for i in range(n): if i not in res: if s[i] >= f[c] and f[i] < m: tmp = i m = f[tmp] if m != max_time: # if no proper activity found, it means the loop will be stopped. res.append(tmp) c = tmp print(f'a{c}: s{s[c]} -> f{f[c]}') return res # s = [0, 10, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12] # f = [0, 14, 5, 6, 7, 9, 9, 10, 11, 12, 14, 16] # a2: s3 -> f5 # a4: s5 -> f7 # a8: s8 -> f11 # a11: s12 -> f16 # [0, 2, 4, 8, 11]