求解活動安排問題
阿新 • • 發佈:2022-03-27
問題
活動選擇問題是一個排程競爭共享資源的多個活動的問題。
設有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]