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)