Uploaded by Eric

贪心算法:概念、应用与实例分析

advertisement
1
貪心演算法
GREEDY ALGORITHM
2
引言
 舉例:
 工作時,可以用最短時間做最多的事。
 出國旅行時,可以用最少的時間玩過最多的地方。
 換零錢時,可以換到最少的硬幣。
…
想想看,生活中還有甚麼樣的範例也適用於這樣的演算法概念
呢?你能不能舉出更多的範例?
3
最佳化問題
 最佳化問題(Optimization problem)觀念:
 一個問題可能有許多解,希望找到一個有最佳數值(最大值或最小
值)的解答,稱為最佳解(或最佳解之一),這類問題稱之為最佳
化問題。
 在所有解決最佳化問題的演算法中,「貪心演算法」是最直覺的一種
方法。
4
貪心演算法
 觀念:
 貪心演算法是每一次選擇中都採取當下看起來最佳的選擇。
 希望透過一次又一次局部(Local)最佳解的選擇,最後綜合成為全部
(Global)最佳解的演算法。
 更多說明:
 每一次選擇是根據某種準則(選擇程序)來決定,而且與前後次的任何
一個決定無關。
 用來解決最佳化問題時,通常很簡單而且有效率。
 不保證有最佳解。
5
推銷員旅行問題
 「推銷員旅行問題」在資訊科學中是一個極為經典的問題(Traveling
salesman problem, TSP),下圖為5個城市的地圖以及彼此間的距
離,某個推銷員希望能從城市A出發,繞經所有城市後再回到城市A,
希望能找出總距離最短的路線。
4
A
3
2
E
1
5
1
2
C
B
3
D
以貪心演算法的觀念來看,從A城市出發的「選擇準則」
為?___________________________。
得到的總距離為?___________________________。
是最佳解嗎?請解釋「是」或「不是」?______________
_____________________________________。
6
推銷員旅行問題
4
A
3
B
2
E
1
5
1
2
C
D
3
7
推銷員旅行問題
 「推銷員旅行問題」在資訊科學中是一個極為經典的問題(Traveling
salesman problem, TSP),下圖為5個城市的地圖以及彼此間的距
離,某個推銷員希望能從城市A出發,繞經所有城市後再回到城市A,
希望能找出總距離最短的路線。
4
A
3
2
E
1
5
1
2
C
B
3
D
以貪心演算法的觀念來看,從A城市出發的「選擇準則」
為?每次在選擇下一個城市的時候,只考慮當前情況,
保證迄今為止經過的路徑總距離最小。
得到的總距離為?13。
是最佳解嗎?請解釋「是」或「不是」?不是,最佳解為11。
8
貪心演算法的演算過程
重複下列步驟直到找出解答為止:
 貪心準則(選擇程序)
 事先設計好一個選擇局部最佳解的準則,挑選出下一個項目加入到
【解集合】中。
 可行性檢查
 檢查加入新項目後的【解集合】是否符合題目的要求。
 解答檢查
 檢查新的【解集合】是否已經是此問題的最終解。
9
貪心演算法的應用-換零錢
 有面額1000、500、100、50、10、5、1元的紙鈔與硬幣,如果有
n元,請用最少的紙鈔與硬幣組成。
 輸入說明:
 每筆測試資料輸入一個正整數n,表示待換的錢,1≤n≤105。
 輸出說明:
 每筆測試資料輸出一行,表示最少的紙鈔與硬幣組成1000、500、
100、50、10、5、1之數量,中間用空格分隔。
10
貪心演算法的應用-換零錢
 有面額1000、500、100、50、10、5、1元的紙鈔與硬幣,如果有
n元,請用最少的紙鈔與硬幣組成。
 輸入範例:
28
1000
500
100
50
10
5
1
1000
500
100
50
10
5
1
1541
 輸出範例:
0000213
1100401
11
貪心演算法的應用-換零錢
 有面額1000、500、100、50、10、5、1元的紙鈔與硬幣,如果有
n元,請用最少的紙鈔與硬幣組成。
 輸入範例:
28
1000
0
500
0
100
0
50
0
10
2
5
1
1
3
1000
1
500
1
100
0
50
0
10
4
5
0
1
1
1541
 輸出範例:
0000213
1100401
12
貪心演算法的應用-換零錢
貪心準則:先換面額較大的紙鈔或硬幣,換完才換較小的。
1.
依序取出每個紙鈔或硬幣面額,假設目前考慮第i個紙鈔或硬幣,面
額為v[i],求「n/v[i]」的整數,新的n值為「除以v[i]的餘數」。
2.
重覆步驟1,直到所有面額算完為止。
以28元為例: ( [ ]表示取整數,%表示求餘數)
可換 [] = ____個10元。
剩下_______ = _____元,可換 [_____] = ____個5元。
剩下_______ = _____元,可換 [_____] = ____個1元。
13
貪心演算法的應用-換零錢
貪心準則:先換面額較大的紙鈔或硬幣,換完才換較小的。
1.
由面額大到小依序取出每個紙鈔或硬幣面額,假設目前考慮第i個紙鈔
或硬幣,面額為v[i],求「n/v[i]」的整數,新的n值為「除以v[i]的餘數」。
2.
重覆步驟1,直到所有面額算完為止。
以28元為例: ( [ ]表示取整數,%表示求餘數)
可換 [28/10] = 2個10元。
剩下28%10 = 8元,可換 [8/5] = 1個5元。
剩下8%5 = 3元,可換 [3/1] = 3個1元。
14
課堂練習:換零錢
 有面額1000、500、100、50、10、5、1元的紙鈔與硬幣,如果有
n元,請用最少的紙鈔與硬幣組成。
v = [1000,500,100,50,10,5,1]
n=int(input())
for i in range(7):
print( int(n/v[i]),end=" " )
n = n % v[i]
15
貪心演算法的應用-換零錢
 想一想,如果多增加了一個18元的面額,請問貪心法則還適用嗎?
 請寫下你認為「適用」或「不適用」的原因?
我認為:▢ 適用
因為:
▢ 不適用
16
貪心演算法的應用-換零錢
 想一想,如果多增加了一個18元的面額,請問貪心法則可以找到最少
的零錢組合嗎?
 請寫下你認為「可以」或「不可以」的原因?
我認為:▢ 可以
■ 不可以
因為:
28:1(18)+1(10)
25:1(18)+1(5)+2(1) 但 25:2(10)+1(5)
所以驗證不可以
17
課堂練習:悠閒的超商店員
 還記得有這樣的經驗嗎?你去買一個10元的東西,你付了1張100元
的鈔票,店員卻找你9個10元,甚至有找你6個10元4個5元和10個1
元的情況。為了避免這樣的情況,某超市在每一間門市都準備了足夠
的零錢,並且要求店員找給顧客的零錢個數一定要是最少的。已知這
個國家使用的貨幣有1、5、10、50、100、500、1000、5000、
10000元等9種,你能幫他解決這個問題嗎?
 輸入說明:
 每筆測試資料輸入一個正整數n ,代表要找的錢, 1≤n≤105 。
 輸出說明:
 每筆測試資料輸出一行,表示找給顧客n元時,最少零錢的個數。
18
課堂練習:悠閒的超商店員
 輸入範例:
17
99
 輸出範例:
4
1個10元+1個5元+2個1元
10
1個50元+4個10元
1個5元+4個1元
請用上一個課堂練習進行改寫
19
課堂練習:悠閒的超商店員
 有面額10000、5000、1000、500、100、50、10、5、1元的紙鈔
與硬幣,如果有 n元,請用最少的紙鈔與硬幣組成。
c=0
v = [______________________]
n = ________________
for (_________________ )
c = c + __________
n = __________
print(c)
20
貪心演算法的應用-工作排程問題
 有n個工作要交給一台機器,每個工作有選定的開始時間與結束時
間,每次只能執行一個工作,而且工作一旦開始就必須完成,不能切
換到另一個工作,假設工作切換時間很快,可以忽略不計,請問最多
可以完成幾個工作?
 輸入說明:
第一行有一個正整數n,表示需要執行的工作個數,n<150。接
下來有n行,每一行有兩個正整數s與e,分別表示工作的開始時
間與結束時間,1≤ s, e≤1000,且s<e。
 輸出說明:
輸出一個數字,表示結束時最多完成幾個工作。
21
貪心演算法的應用-工作排程問題
 輸入範例:
6
1 11
69
26
8 10
48
14
 輸出範例:
3
time
Job
1
2
3
4
5
6
1
2
3
4
5
6
7
8
9
10
11
22
貪心演算法的應用-工作排程問題
 先做結束工作最早的工作6。
 再選擇開始時間等於或晚於工作6結束時間的工作5。
 最後選擇工作4。
 可完成的工作共有3件。
time
Job
6
5
4
1
2
3
4
5
6
7
8
9
10
11
23
貪心演算法的應用-工作排程問題
貪心準則:
結束時間最早的工作,而且沒有與已經執行的工作衝突的工作先做。
 先將n個工作以_______的先後來排序。
 由最前面取出第1個工作,工作取出後,表示這個工作送進機器執
行,可以執行的工作數增加1。
 所有剩餘的工作中,若開始時間早於正在執行工作的結束時間,則放
棄執行此工作,直到找到開始時間等於或晚於正在執行工作的結束時
間,將找到的工作放進機器執行,可以執行的工作數增加1。
 重覆上述步驟直到檢查完所有工作,即可得到最多可完成的工作數。
24
貪心演算法的應用-工作排程問題
貪心準則:
結束時間最早的工作,而且沒有與已經執行的工作衝突的工作先做。
 先將n個工作以結束時間的先後來排序。
 由最前面取出第1個工作,工作取出後,表示這個工作送進機器執
行,可以執行的工作數增加1。
 所有剩餘的工作中,若開始時間早於正在執行工作的結束時間,則放
棄執行此工作,直到找到開始時間等於或晚於正在執行工作的結束時
間,將找到的工作放進機器執行,可以執行的工作數增加1。
 重覆上述步驟直到檢查完所有工作,即可得到最多可完成的工作數。
25
課堂練習:工作排程
 有n個工作要交給一台機器,每個工作有選定的開始時間與結束時
間,每次只能執行一個工作,而且工作一旦開始就必須完成,不能切
換到另一個工作,假設工作切換時間很快,可以忽略不計,請問最多
可以完成幾個工作?
輸入範例:
6
1 11
69
26
8 10
48
14
輸出範例:
3
就上頁範例而言
課堂練習:工作排程
n=int(input())
S=[0 for i in range(n)]
E=[0 for i in range(n)]
for i in range(n):
s,e = map(int,input().split())
S[i]=s
E[i]=e
s:start time
e:end time
for i in range(n-1 ) :
for j in range(n-1-i):
if E[j]>E[j+1]:
temp=E[j]
E[j]=E[j+1]
E[j+1]=temp
temp=S[j]
S[j]=S[j+1]
S[j+1]=temp
count=1
end=E[0]
for i in range(1,n):
if (S[i]>=end):
count=count+1
end =E[i]
print(count)
index
S
0
261
E
1
2
6
2
4
8
3
6
9
4
8
10
5
1
11
4
27
貪心演算法的應用-誰先晚餐
 有n個人在同一家餐廳吃晚餐,每個人點的餐,廚師煮的時間都不一
樣,而每個人吃完時間也不一樣。而餐廳一次只能煮一道菜,請問廚
師要如何安排煮的順序,才能最早讓所有人都吃完離開。
 輸入說明:
 第一行為一個整數n,代表用餐人數,n≤105。接下來有n行,每行有兩
個正整數c與e,分別代表每個人的餐點準備時間與食用時間,1≤c,e≤
105。
 輸出說明:
 輸出結果,表示在廚師烹煮的最佳情況下,最後一個人的離開時間。
28
貪心演算法的應用-誰先晚餐
 有n個人在同一家餐廳吃晚餐,每個人點的餐,廚師煮的時間都不一
樣,而每個人吃完時間也不一樣。而餐廳一次只能煮一道菜,請問廚
師要如何安排煮的順序,才能最早讓所有人都吃完離開。
解題關鍵:你的貪心準則是甚麼?
▢ 煮最慢的先做
▢ 煮最快的先做
▢ 吃最快的先做
▢ 吃最慢的先做
29
貪心演算法的應用-誰先晚餐
 有n個人在同一家餐廳吃晚餐,每個人點的餐,廚師煮的時間都不一
樣,而每個人吃完時間也不一樣。而餐廳一次只能煮一道菜,請問廚
師要如何安排煮的順序,才能最早讓所有人都吃完離開。
解題關鍵:你的貪心準則是甚麼?
▢ 煮最慢的先做
▢ 煮最快的先做
▢ 吃最快的先做
■ 吃最慢的先做
30
貪心演算法的應用-誰先晚餐
 如果輸入下列數字,需要多少時間才能讓所有人離開?
Case 1:
2
11 8
7 10
Case 2:
3
1 1
2 2
3 3
Case 3:
3
9 18
4 6
7 10
31
貪心演算法的應用-誰先晚餐
 如果輸入下列數字,需要多少時間才能讓所有人離開?
Case 1:
2
11 8
7 10
Case 2:
3
1 1
2 2
3 3
Case 3:
3
9 18
4 6
7 10
Ans:
26
Ans:
7
Ans:
27
課堂練習:誰先晚餐
n=int(input()) # 客人數量
C=[0 for i in range(n)] #準備時間
E=[0 for i in range(n)] #食用時間
for i in range(n):
C[i],E[i]=map(int,input().split())
cooktime=0
totaltime=0
# 依照食用時間排序 由大到小
for i in range(n-1 ) :
for j in range(n-1-i):
if E[j]<E[j+1]:
temp=E[j]
E[j]=E[j+1]
E[j+1]=temp
temp=C[j]
C[j]=C[j+1]
C[j+1]=temp
for i in range(n):
cooktime = cooktime + C[i]
if (cooktime+E[i]>totaltime):
totaltime=cooktime+E[i]
print(totaltime)
32
33
貪心演算法的應用-背包問題
 只要做貪心演算法,一定會遇到的經典題 -背包問題 (Fractional
Knapsack)。
 假設有n個物品及一個背包,已知背包的負重能力與每個物品的價值
與重量,可以將物品分割後,只取部分放入背包,求在背包的負重能
力範圍內,放入背包所有物品的最大價值。
34
貪心演算法的應用-背包問題
 輸入說明:
 第一行有一個正整數n,表示物品個數,n<100。
接下來有n行,每一行有兩個正整數w與v,分別
表示物品的重量與價值,1≤w, v≤100。最後有
一個正整數k,表示背包的負重能力, k<1000 。
輸入範例:
3
6 12
44
7 21
15
 輸出說明:
 輸出一個數值(四捨五入至小數第二位),表示在背
包的負重能力範圍內,放入背包所有物品的最大
價值。
輸出範例:
35.00
35
貪心演算法的應用-背包問題
 想一想:
貨物 ❶
重量:6
價值:12
貨物 ❷
重量:4
價值:4
放入背包所有物品的最大價值
_____________。
貨物 ❸
重量:7
價值:21
解題關鍵:
你的貪心準則是甚麼?
36
貪心演算法的應用-背包問題
 想一想:
貨物 ❶
重量:6
價值:12
貨物 ❷
重量:4
價值:4
放入背包所有物品的最大價值
35。
貨物 ❸
重量:7
價值:21
解題關鍵:
你的貪心準則是甚麼?
先計算所有物品的單位重
量的價值,將所有物品以
單位重量的價值由大到小
進行排序,取最大的單位
重量的價值物品開始放。
37
貪心演算法的應用-背包問題
 貪心準則:先計算所有物品的單位重量的價值,將所有物品以單位重
量的價值由大到小進行排序,取最大的單位重量的價值物品開始放。
 若可以放進背包就放入背包,一直到最後放不下或所有物品都已放
入為止。
 最後放不下的部分,取剩餘未放入物品的最高單位重量的價值物品
填滿到背包重量的上限,即可求得背包的負重能力範圍內,放入背
包所有物品的最大價值。
38
貪心演算法的應用-背包問題
 步驟1:選擇單位重量的價值最大的物品3先放,因為背包負重大於物品3的
重量,所以可全部放入。
 15 - 7 = 8,背包負重剩8。
 價值為0 + 21 = 21。
 步驟2:因為背包還沒滿,選擇第
物品
重量
價值
1
2
3
6
4
7
12
4
21
單位重量的價值
( = 價值/重量 )
2
1
3
二大的物品1放入,背包剩餘負重
還是大於物品1,所以可全放入。  步驟3:背包還沒滿,繼續放,物品2
 8 - 6 = 2,背包負重剩2。
無法全部放入,只能放_____單位。
 價值為21 + 12 = 33。
 價值為________________。
39
貪心演算法的應用-背包問題
 步驟1:選擇單位重量的價值最大的物品3先放,因為背包負重大於物品3的
重量,所以可全部放入。
 15 - 7 = 8,背包負重剩8。
 價值為0 + 21 = 21。
 步驟2:因為背包還沒滿,選擇第
物品
重量
價值
1
2
3
6
4
7
12
4
21
單位重量的價值
( = 價值/重量 )
2
1
3
二大的物品1放入,背包剩餘負重
還是大於物品1,所以可全放入。  步驟3:背包還沒滿,繼續放,物品2
 8 - 6 = 2,背包負重剩2。
無法全部放入,只能放2單位。
 價值為21 + 12 = 33。
 價值為35。
40
貪心演算法的應用-吃水果增強免疫力
n=int(input()) # 物品種類數量
V=[0 for i in range(n)] #物品價值
W=[0 for i in range(n)] #物品重量
CP=[0.0 for i in range(n)] #物品CP
for i in range(n):
W[i],V[i]=map(int,input().split())
CP[i] = V[i]/W[i]
k=int(input()) # 背包可放的重量
totalv=0
# 依照CP 由大到小
for i in range(n-1 ) :
for j in range(n-1-i):
if CP[j]<CP[j+1]:
temp=CP[j]
CP[j]=CP[j+1]
CP[j+1]=temp
temp=V[j]
V[j]=V[j+1]
V[j+1]=temp
temp=W[j]
W[j]=W[j+1]
W[j+1]=temp
#由CP值大的物品開始放
for i in range(n):
if (W[i]<k):
totalv=totalv+V[i]
k = k - W[i]
else:
totalv = totalv+CP[i]*k
break
print(totalv)
41
不適用貪心演算法的應用-0/1背包問題
 0/1背包問題:假設有n個物品及一個背包,已知背包的負重能力與每
個物品的價值與重量,每個物品只能取或不取,無法只取一部分放入
背包,求在背包的負重能力範圍內放入背包所有物品的最大價值。
 不是所有問題都可以使用貪心演算法解題,有很多問題不適合使用貪
心演算法。
物品
1
2
3
重量 價值
單位重量的價值
( = 價值/重量 )
6
12
2
8
14
1.75
7
21
3
假設背包最多負重15KG
貪心演算法
最佳解
12
21
14
21
33
35
42
貪心演算法的應用-吃水果增強免疫力
 隨著全球武漢肺炎疫情持續延燒,全球呈現戒備狀態,除了勤洗手、
戴口罩與環境進行多消毒,提升個人的身體免疫力至關重要,特別是
維生素C,可透過蔬菜水果的食用來攝取。
 右表為常見水果中維生素C含量最高的
水果與價格,請問在希望每天水果花
費不超出m元的前提之下,應購買那
些水果才能獲得最高的維生素C含量?
(假設水果可以任意切片購買所需的克
數,每種水果攝取量不超過100克)
水果
每100克含維生素C
含量(mg)
每100克價格
柳橙
58
5
檸檬
56
7
鳳梨
80
4
草莓
85
20
奇異果
90
10
芭樂
245
8
43
貪心演算法的應用-吃水果增強免疫力
 輸入說明:
 第一行有一個正整數n,表示水果的種類數,n<100。接下來有n行,每
一行有兩個正整數c與p,分別表示每100克含維生素C含量(mg)與每
100克價格,1≤c,p≤300。最後有一個正整數m,表示每天水果的最
高花費限制, m<1000 。
 輸出說明:
 輸出一個數值(四捨五入至小數第二位),表示在每天水果的花費範圍內,
可以攝取的最多維生素C含量。
44
貪心演算法的應用-吃水果增強免疫力
 請先算出每一樣水果單位價格下的維生素含量(CP值):
水果
每100克含維生素
C含量(mg)
每100克價格
柳橙
58
5
檸檬
56
7
鳳梨
80
4
草莓
85
20
奇異果
90
10
芭樂
245
8
單位價格之
維生素C含量
解題關鍵:
你的貪心準則是甚麼?
45
貪心演算法的應用-吃水果增強免疫力
 請先算出每一樣水果單位價格下的維生素含量(CP值):
水果
每100克含維生素
C含量(mg)
每100克價格
單位價格之
維生素C含量
柳橙
58
5
11.6
檸檬
56
7
8
鳳梨
80
4
20
草莓
85
20
4.25
奇異果
90
10
9
芭樂
245
8
30.625
解題關鍵:
你的貪心準則是甚麼?
先計算所有水果的單位價
格的維生素C含量,將所
有水果以單位價格的C含
量由大到小進行排序,取
最大的單位價格的C含量
水果開始放。
46
貪心演算法的應用-吃水果增強免疫力
 輸入範例:
輸出的結果應該是多少?
6
58 5
56 7
80 4
85 20
6種水果
每100公克之維他命C含量
每100公克之價格
90 10
245 8
30
假設購買水果的花費最多30元
即花費30元的狀況下,最多
可攝取?mg的維生素C。
47
貪心演算法的應用-吃水果增強免疫力
 輸入範例:
輸出的結果應該是多少?
6
58 5
56 7
80 4
85 20
6種水果
每100公克之維他命C含量
每100公克之價格
90 10
245 8
30
假設購買水果的花費最多30元
即花費30元的狀況下,最多可
攝取497.00mg的維生素C。
=245+80+58+90+24
(8) (4) (5) (10) (3)
Download