動態規劃-計數dp-2262. 字串的總引力
title: 活動選擇問題
date: 2022-05-09 14:40:50
tags: 演算法 貪心策略 活動選擇問題
活動選擇問題
無權值
問題背景
有一個會場,需要安排幾場活動
- 公司年會:10:00 - 19:00
- 婚禮宴請:11:00- 14:00
- 生日聚會:12:00 - 17:00
- 學術研究:14:00 - 16:00
問 如果安排這個會場能讓活動儘可能的多的進行?
有下面這幾個活動和佔比時間
選擇出租的時間不能有衝突
問題研究,策略選擇
這時候我們採用貪心策略:
-
策略一:最短活動優先
-
策略二:最早開始的活動優先
- 策略三:最早結束活動優先
選擇最早結束的活動,可以給後面的活動留出更大的弓箭
- 證明該策略是正確的(替換法)
我們可以通過替換的規則讓 某個最優解替換為 貪心所得的最優解,在替換的時候,如果活動相同的話,直接替換即可,如果不相同的話,因為貪心所得的是這個活動的最先完成 的活動,所有會給後面的活動留出的活動空間更大不會影響後面活動的替換
程式碼實現
輸入:活動集合S={a1, a2, a3 .. an},每個活動ai的起始時間si 結束時間 fi
輸出:不衝突的最大子集S‘
先把活動結束時間由小到大排個序
先給陣列S賦值 a1(因為這裡a1是最早活動的結束時間)
令K = 1
for i 迴圈 從2 到最後
如果 ai 的起始時間 小於 ak的結束時間的話,就把ai新增到S’中, 把 i賦值給k
最後返回 S
import random # 活動選擇問題 # 我們是用元組來初始化資料(開始時間, 結束時間) # 按照活動結束時間從小到大排序 def activity_sort(data): # data中的資料都是按照活動結束時間從小到大排序的,也就是說a1是結束時間最早的,依次排序 S = [] S.append(data[0]) print("事件1") k = 0 for i in range(1, len(data)): # 如果第i個時間的開始時間 小於第k個時間的結束時間的話,則表明這個時間他衝突,跳過 if data[i][0] >= data[k][1]: print(f"事件{i+1}") k = i return S # 快速排序 def quick_sort(data, start, end): if start > end: return 0 # 先獲取一個隨機數當做主元 prime_index = random.randint(start, end) # 把這個隨機數放到最後方便計算 data[end], data[prime_index] = data[prime_index], data[end] # 尋找該主元的正確的下標 # 下標i以後的為小於主元元素的 # 下標j以後i以前的表示大於該元素的 i = start-1 j = start while j < end: # 如果j 元素比主元小的話,就讓i+1元素和j元素互換位置,並讓i+=1 j+=1 # 因為:下標i以後的為小於主元元素的,下標j以後i以前的表示大於該元素的 if data[j][1] <= data[end][1]: data[i+1], data[j] = data[j], data[i+1] i += 1 j += 1 else: j += 1 # 最後吧主元放到對應的位置 data[i+1], data[end] = data[end], data[i+1] # 進行遞迴呼叫 quick_sort(data, start, i) quick_sort(data, i+2, end) data = [(1,4,1),(0,6,4), (3,5,6),(2,14,8), (3,9,3), (4,7,7),(5,9,12), (6,10,2), (8,11,9),(8,12,11)] quick_sort(data, 0, 9) activity_sort(data)
有權值
上述問題我們預設為每個事件的收入都是 1, 所以我們關注一共有多少個時間的發生:
所以相比於上面的問題我們多了一個權重:
這時候如果我們在向上面的方式來進行選擇問題的話,這時候可能就會得不償失
如果按照上述方法進行第一個問題的話 我們選擇的權值和為2, 但是實際上我們想要的權值和為 10000
問題分析
這時候我們需要進行動態規劃 + 貪心演算法來求解這個問題, 選擇a6 和 a5的時候我們可以根據前面的最優問題來進行求解
問題預處理
這裡我們需要定義一個
p陣列 p[i] 表示在a[i]開始前最後結束的活動,
p陣列目的:為了防止活動衝突
D[i]陣列:集合{a1, a2, a3.... an}中不衝突活動的最大權值和
遞推關係建立
當我們在ai的時候,
- 選擇ai 那麼 D[i] = Wai + D[p[i]]
- 不選擇ai D[i] = D[i-1]
- 遞推公式:D[i] = max{D[pi] + w, D[i-1]}
計算過程
P陣列表示 ai 發生之前的最晚發生的事件
D陣列表示權值的最大
Rec 1表示選擇這個活動, 0表示不選擇這個活動
程式碼實現
import random
# 活動選擇問題
# 我們是用元組來初始化資料(開始時間, 結束時間)
# 按照活動結束時間從小到大排序
def activity_sort(data, D, Rec, p):
# 進行D陣列和Rec陣列
# D陣列:權值選擇問題
# Rec陣列:是否選擇改事件
# D[i] 的依據是 max{D[i-1], D[p1] + wi}
for num_index in range(len(data)):
if D[num_index] > (D[p[num_index]] + data[num_index][2]):
D[num_index+1] = D[num_index]
Rec[num_index] = 0
else:
D[num_index+1] = D[p[num_index]] + data[num_index][2]
Rec[num_index] = 1
# 陣列追蹤
print(f"事件總權重為{D[len(D)-1]}")
i = len(Rec)-1
while i >= 0:
if Rec[i] == 1:
print(f"選擇事件{i+1},權重{data[i][2]}")
i = p[i]-1
continue
i -= 1
# 快速排序
def quick_sort(data, start, end):
if start > end:
return 0
# 先獲取一個隨機數當做主元
prime_index = random.randint(start, end)
# 把這個隨機數放到最後方便計算
data[end], data[prime_index] = data[prime_index], data[end]
# 尋找該主元的正確的下標
# 下標i以後的為小於主元元素的
# 下標j以後i以前的表示大於該元素的
i = start-1
j = start
while j < end:
# 如果j 元素比主元小的話,就讓i+1元素和j元素互換位置,並讓i+=1 j+=1
# 因為:下標i以後的為小於主元元素的,下標j以後i以前的表示大於該元素的
if data[j][1] <= data[end][1]:
data[i+1], data[j] = data[j], data[i+1]
i += 1
j += 1
else:
j += 1
# 最後吧主元放到對應的位置
data[i+1], data[end] = data[end], data[i+1]
# 進行遞迴呼叫
quick_sort(data, start, i)
quick_sort(data, i+2, end)
# 初始化p陣列
def init_p(data):
p = [0 for i in range(len(data))]
for i in range(len(data)):
# k表示第i個時間的起始時間
start_time = data[i][0]
start = 0
end = len(data)-1
flag = 0
while(start < end):
mid = start + int((end - start)/2)
# 如果中間事件的結束時間小於要查詢的事件
if data[mid][1] == start_time:
flag = mid
break
elif data[mid][1] > start_time:
end = mid-1
else:
start = mid + 1
if flag != 0:
p[i] = flag+1
elif data[start][1] > start_time:
p[i] = start
else:
p[i] = start+1
return p
data = [(1,4,1),(0,6,4), (3,5,6),(2,14,8), (3,9,3), (4,7,7),(5,9,12), (6,10,2), (8,11,9),(8,12,11)]
quick_sort(data, 0, 9)
D = [0 for i in range(len(data) + 1)]
Rec = [0 for i in range(len(data))]
# 初始化P陣列 p[i] 表示事件i開始前最晚結束的事件
p = init_p(data)
activity_sort(data, D, Rec, p)
print(D)
print(Rec)
print(p)