貪婪(Greedy)演算法

什麼是貪婪(Greedy)演算法?

其實已經在排序演算法使用過了,使用選擇排序將10個數字由小到大排序,每次選最大的元素放到第10個位置,縮小範圍到前9個數字,將前9個數字最大的放到第9個位置,縮小範圍到前8個數字,將前8個數字最大的放到第8個位置,依此類推,縮小範圍到前2個數字,將前2個數字較大的放到第2個位置,剩下1個就不用再調整,到此完成排序,這就是一種貪婪演算法,策略是目前範圍內的最大值,移到目前範圍的最後,一定能將10個數字由小到大排序。

貪婪演算法只考慮目前狀態最佳的選擇,且之前所選擇的解答不會影響後面所選擇的解答,不斷的選擇局部的最佳解,全部選取結束後就獲得整體的最佳解。若可以舉出反例,就證明所使用的貪婪演算法中的貪婪準則是不正確的。

貪婪演算法的解題步驟

Step1)定義貪婪準則

根據此貪婪準則,不斷找出目前小問題的最佳解。

Step2)不斷重複貪婪準則,直到解決問題為止

不斷的使用貪婪準則,,找出每個小問題的最佳解,直到解決問題為止。

貪婪演算法有其限制性

貪婪演算法依照事先定義好的貪婪準則,依照此貪婪準則每次選擇目前小問題的最佳解後,就不能再修正,這一次選取的物品與下一次選取的物品沒有關係,最後這些被選取的物品的集合就是最佳解。有些問題適合使用貪婪演算法,有些問題是無法找到貪婪準則,就不能使用貪婪演算法進行解題。


以下舉例貪婪演算法

(1)最多有幾個工作可以執行

有n個工作可以執行,給定每個工作的開始時間與結束時間,時間從0開始,開始與結束時間都是整數,只有一台機器可以執行,每次只能執行一個工作,且工作開始做就需要做完,機器執行中不能跳到另一個工作,可以一結束就馬上接著執行另一個工作,機器更換工作很快,可以不考慮切換所需時間,請計算執行完後最多有幾個工作被完成?

輸入說明

每次輸入數字n,n表示需要執行的工作個數,輸入n小於100,之後有n行分別是每一行兩個整數s與e,s表示工作的開始時間與e表示工作的結束時間,且s永遠小於e。

輸出說明

輸出一個數字,表示結束時最多有幾個工作被完成。

輸入範例

5

1 10

2 4

3 6

2 5

4 9

輸出範例

2

解題想法 

貪婪準則是將結束時間最早的工作先做,讓機器可以空下來準備做其他工作。先將n個工作以結束時間較早的工作排在前面,利用此原則排序好n個工作。 

(1)由最前面取出第一個工作,工作取出後表示這個工作送入機器執行,可以執行的工作數增加1。 

(2)所有剩餘的工作中,若開始時間早於正在執行工作的結束時間,就放棄執行這些工作,直到找到開始時間等於或晚於正在執行工作的結束時間,將找到的工作放入機器執行,可以執行的工作數增加1。 

(3)繼續將所有剩餘的工作中,若開始時間早於正在執行工作的結束時間,就放棄執行這些工作,直到找到開始時間等於或晚於正在執行工作的結束時間,將找到的工作放入機器執行,可以執行的工作數增加1。

 (4)不斷測試到n個工作檢查完,就可以獲得最多可以完成的工作數。

參考程式碼

(2)最多有幾台機器一起運作

有n個工作可以執行,給定每個工作的開始時間與結束時間,工作時間可能重疊,時間從0開始,開始與結束時間都是整數,有n台機器可以執行,每台機器同時間只可以執行一個工作,工作可以到每台機器去執行,且工作開始做就需要做完,機器執行中不能跳到另一個工作,可以一結束就馬上接著執行另一個工作,機器更換工作很快,可以不考慮切換所需時間,執行完後最少需要幾台機器才能完成所有工作?

輸入說明

每次輸入數字n,n表示需要執行的工作個數,輸入n小於100,之後有n行分別是每一行兩個整數s與e,s表示工作的開始時間與e表示工作的結束時間,且s永遠小於e。

輸出說明

輸出一個數字,表示需要幾台機器才能在指定的時間執行完所有工作。

輸入範例

5

1 10

2 4

3 6

2 5

4 9

輸出範例

4

解題想法

貪婪準則是先將n個工作以開始時間最早的工作排在前面,若開始時間相同就以最早結束的工作排在前面進行排序,將排序好的工作,由最前面依序取出每個工作,優先分配到目前已經執行完畢或沒有工作可以執行的機器,若全部機器都有工作正在執行就需要啟用新的機器。需使用陣列m,紀錄每個機器執行目前工作完成後的時間,才能判斷機器是否有空可以執行下一個工作,還是要啟動新的機器。

參考程式碼

(3) 最小平均等待時間

有n個工作從開始就進入到機器,給予n個工作的執行需要的時間,只有一台機器可以執行,每次只能執行一個工作,且工作開始做就需要做完,機器執行中不能跳到另一個工作,請計算完成所有工作所需要的最小平均等待時間?

輸入說明

每次輸入數字n,n表示需要執行的工作個數,輸入n小於100,之後有n行分別是每一行一個整數x,x表示工作執行完成所需時間。

輸出說明

輸出一個浮點數,表示執行所有工作的最小平均等待時間。

輸入範例

5

10

4

6

5

9

輸出範例

10.4

解題想法

貪婪準則是最少執行時間的工作優先執行。由小到大排序所工作的執行時間,執行時間越短的工作越優先執行,最後統計每個工作的等待時間,計算平均等待時間就可獲得最小平均等待時間。

參考程式碼

(4)物品可以分割的背包(Fractional Knapsack)問題

假設有n個物品及一個背包,已知背包的負重能力與每個物品的價值與重量,可以將物品只取部分放入背包,求在背包的負重能力範圍內的放入背包所有物品的最大價值。

輸入說明

每次輸入數字n,n表示物品個數,輸入n小於100,之後有n行分別是每一行兩個整數w與v,w表示物品的重量,而v表示物品的價值。最後輸入一個整數k,表示背包的負重能力。

輸出說明 

輸出一個浮點數,表示在背包的負重能力範圍內的放入背包所有物品的最大價值。

輸入範例

5

3 10

3 4

1 5

2 7

3 8

5

輸出範例

18.6667

解題想法

貪婪準則是先計算所有物品的單位重量的價值將所有物品以單位重量的價值由大到小進行排序,取最大的單位重量的價值物品開始,若可以放進背包就放入背包,一直到最後放不下為止或所有物品都已經放入,最後放不下的部分取剩餘未放入物品的最高單位重量的價值物品,取其中一部分到背包重量的上限為止,到此求得背包的負重能力範圍內的放入背包所有物品的最大價值。

參考程式碼

範例練習1     *c134-UVa- Parliament

內容 :

有一個叫做傻人國的國家,他們的國會要召開新會期了。國會總共有N個議員。根據現行的法規議員們被分在不同的委員會(每個議員僅能待在一個委員 會),且每個委員會的人數均不相同。每天每一個委員會都需推派一名代表參加協調會,而且協調會每天的組成都需不同。國會只有在以上的條件成立之下才能運 作。

你的任務是寫一個程式來幫這些議員分組(總共分多少組,每個組多少人),使得國會能夠運作的天數最大。

輸入說明 : 

輸入的第一列有一個整數代表以下有多少組測試資料。

每組測試資料一列,含有1個整數 N(5 <= N <= 1000)。

第一列與第一組測試資料以及各組測試資料間均有一空白列,請參考Sample Input。

輸出說明 : 

對每組測試資料輸出一列,包含委員會的數目以及各委員會的人數,使得國會能夠運作的天數最大。各委員會輸出的順序按人數由小到大輸出。以第一組測試資料為例說明:31個委員共被分成6組,各組人數分別為2、3、5、6、7、8。

各組測試資料間亦請輸出一空白列,輸出格式請參考Sample Output。

範例輸入 : 

3

31

5

8

範例輸出 :

2 3 5 6 7 8

2 3

3 5

提示 : 

* Luck 貓翻譯

出處 : 

ACM 668


解題策略

貪婪演算法(Greedy)

將n切割成不同數字,且各數字的積最大 

step1  k+1無法達到則結束

       表示將n先分給2,3,4,5, ... ,k,若n-(2+3+...+k)<k+1則結束 

step2  將剩餘n-(2+3+...+k)由k開始平均分給

       k,k-1,k-2..2用完為止

step3  若還有剩餘,也只會剩一個,分給k(最後一位)

範例練習2    *105北市賽 手續費  Huffman編碼

問題描述

全國最大的便利商店正在舉行集點數換獎品的活動,任何參與者皆可以依據其收集到的貼紙點數,換得相對應的獎品。根據該活動的活動辦法,我們知道點數越高所能換得的獎品價值越高;但是,每次兌換獎品時,只限使用一張集點卡上所收集到的點數,不能合併多張集點卡的點數使用。

小智目前手上共有 n 張集點卡,為了換取最高價值的獎品,決定尋求科技公司的協助,將這些集點卡上的所有點數合併到單一集點卡上。然而,這家科技公司的服務有兩項規定:

1) 一次只能合併兩張集點卡,

2) 每次進行合併時,需收取和合併後點數相同數目的手續費。

例如,若小智手上有 3 張集點卡,上面的點數分別是 1, 3, 8。若小智第一次合併點數為 1 和 8 的集點卡,則合併後的新集點卡點數為 9,同時必須付 9 元的手續費;接著,小智要合併點數為 3 和 9 的集點卡,合併後的點數為 12,且手續費為 12 元。因此,兩次合併的手續費總和為 9 + 12 = 21 元。但是,若小智第一次合併點數為 1 和 3 的集點卡,則合併後的新集點卡點數為 4,且手續費為 4 元;接著,小智合併點數為 4 和8 的集點卡,合併後的點數為 12,且手續費為 12 元。因此,兩次合併的手續費總和為 4+ 12 = 16 元。

小智發現不同的合併順序,並不會影響最後合併完成時集點卡上的總點數,但卻會影

響合併所需的手續費。請你幫忙小智計算合併 n 張集點卡的最低手續費。

輸入格式

輸入的第一行有一個整數 n,代表需要合併的集點卡張數;第二行有 n 個以空白符號相間隔的正整數,分別代表這 n 張集點卡原有收集到的點數。同時,輸入資料中,每一張集點卡上原有收集到的點數,皆為小於或等於 1,000 的正整數。

輸出格式

請根據輸入的資料,輸出合併這 n 張集點卡所需最低手續費。


輸入範例 1

3

1 3 8

輸出範例 1

16

輸入範例 2

4

8 3 5 1

輸出範例 2

30

解題策略

Huffman編碼

範例練習3    *c110: Packets

內容 :

有一間工廠生產的東西, 被包裝在相同高度 h 的正方形容器內, 但其面積大小分別有:1*1, 2*2, 3*3, 4*4, 5*5, 6*6等六種尺寸。這些產品總是用高度為h,面積為6*6的箱子打包後寄給客戶。因為成本關係,當然希望將客戶所訂購的產品放在最少的箱子裡寄出。請你寫 一個程式找出寄送這些產品最少需要多少個箱子,這可以使工廠節省下不少錢。

輸入說明 :

每組測試資料一列(就是一份訂單),含有6個整數。分別代表1*1到6*6產品的數目。若此6個整數均為0代表輸入結束。

輸出說明 :

對每一組測試資料,輸出寄送這些產品最少需要多少個箱子。

範例輸入 :

0 0 4 0 0 1

7 5 1 0 0 0

0 0 0 0 0 3

79 96 94 30 18 14

53 17 12 98 76 54

83 44 47 42 80 3

15 26 13 29 42 40

41 61 36 90 54 66

0 0 0 0 0 0

範例輸出 :

2

1

3

86

231

137

115

219

提示 :

* Luck 貓翻譯

出處 :

ACM 311

解題策略

貪婪(Greedy)

處理6x6

處理5x5 剩餘的給1x1

處理4x4 剩餘的給2x2與1x1

處理3x3 剩餘的給2x2與1x1

處理2x2 剩餘的給1x1

處理1x1

範例練習4    APCS10610第4題物品堆疊 

問題描述

某個自動化系統中有一個存取物品的子系統,該系統是將 N 個物品堆在一個垂直的貨架上,每個物品各佔一層。系統運作的方式如下:每次只會取用一個物品,取用時必須先將在其上方的物品貨架升高,取用後必須將該物品放回,然後將剛才升起的貨架降回原始位置,之後才會進行下一個物品的取用。

每一次升高某些物品所需要消耗的能量是以這些物品的總重來計算,在此我們忽略貨架的重量以及其他可能的消耗。現在有 N 個物品,第 i 個物品的重量是 w(i)而需要取用的次數為 f(i),我們需要決定如何擺放這些物品的順序來讓消耗的能量越小越好。舉例來說,有兩個物品 w(1)=1、w(2)=2、f(1)=3、f(2)=4,也就是說物品 1 的重量是 1 需取用 3次,物品 2 的重量是 2 需取用 4 次。我們有兩個可能的擺放順序(由上而下):

 (1,2),也就是物品 1 放在上方,2 在下方。那麼,取用 1 的時候不需要能量,而每次取用 2 的能量消耗是 w(1)=1,因為 2 需取用 f(2)=4 次,所以消耗能量數為w(1)*f(2)=4。

 (2,1),也就是物品 2 放在 1 的上方。那麼,取用 2 的時候不需要能量,而每次取用1的能量消耗是w(2)=2,因為1需取用f(1)=3次,所以消耗能量數=w(2)*f(1)=6。

在所有可能的兩種擺放順序中,最少的能量是 4,所以答案是 4。再舉一例,若有三物品而 w(1)=3、w(2)=4、w(3)=5、f(1)=1、f(2)=2、f(3)=3。假設由上而下以(3,2,1)的順序,此時能量計算方式如下:取用物品 3 不需要能量,取用物品 2 消耗 w(3)*f(2)=10,取用物品 1 消耗(w(3)+w(2))*f(1)=9,總計能量為 19。如果以(1,2,3)的順序,則消耗能量為3*2+(3+4)*3=27。事實上,我們一共有 3!=6 種可能的擺放順序,其中順序(3,2,1)可以得到最小消耗能量 19。

輸入格式

輸入的第一行是物品件數 N,第二行有 N 個正整數,依序是各物品的重量 w(1)、w(2)、...、w(N),重量皆不超過 1000 且以一個空白間隔。第三行有 N 個正整數,依序是各物品的取用次數 f(1)、f(2)、...、f(N),次數皆為 1000 以內的正整數,以一個空白間隔。

輸出格式

輸出最小能量消耗值,以換行結尾。所求答案不會超過 63 個位元所能表示的正整數。

範例一(第 1、3 子題):輸入

2

20 10

1 1

範例一:正確輸出

10

範例二(第 2、4 子題):輸入

3

3 4 5

1 2 3

範例二:正確輸出

19


解題策略

貪婪(Greedy)

先找出物品的順序,若物品a與b,先以a.w*b.f < b.w*a.f排序,也就是本身重量(a.w)與對方頻率(b.f)相乘的值越小的物品優先放在上層,有了順序就可以計算最小消耗能量。

參考資料

https://henrybear327.github.io/CodingNotes/contest/apcs/1061028/

範例練習5    APCS202001 第3題砍樹  貪婪+堆疊

有一個長度為 L 的長條型的林場,N 棵樹種在一排,現階段砍樹必須符合以下的條件:「讓它向左或向右倒下,倒下時不會超過林場的左右範圍之外,也不會壓到其它尚未砍除的樹木。」你的工作就是計算能砍除的樹木。

 若 ci 代表第 i 棵樹的位置座標,hi 代表高度。向左倒下壓到的範圍為 [ci−hi,ci],而向右倒下壓到的範圍為 [ci,ci+hi]。 如果倒下的範圍內有其它尚未砍除的樹就稱為壓到,剛好在端點不算壓到。 我們可以不斷找到滿足砍除條件的樹木,將它砍倒後移除,然後再去找下一棵可以砍除的樹木,直到沒有樹木可以砍為止。

無論砍樹的順序為何,最後能砍除的樹木是相同的。


輸入說明

第一行為正整數 N 以及一個正整數 L,代表樹的數量與林場右邊界的座標。N≤10^5,L≤10^9 。 

第二行有 N 個正整數依序代表這 N 棵樹的座標,座標是從小到大排序的,所有座標不超過 L。

第三行有 N 個正整數依序代表樹的高度,所有樹高都不超過 10^9。 

本題包含三個子題組,每個子題組配分如下: 

第 1 子題組共 20 分: N≤10 

第 2 子題組共 40 分: N≤1000 

第 3 子題組共 40 分: 無額外限制。

輸出說明

輸出共有兩行,第一行輸出能被砍除之樹木數量,第二行輸出能被砍除之樹木中最高的高度,如果無法砍除任何一棵樹,則最高高度輸出 0。


輸入範例1

6 140

10 30 50 70 100 125

30 15 55 10 55 25

輸出範例1

30


輸入範例2

1 10

5

6

輸出範例2

0

0

解題策略

由左到右掃描所有樹,將不可移除的樹加入堆疊。考慮新的樹,如果堆疊內有可以移除(向右倒下,連鎖反應)的樹就移除,接著檢查新的樹是否可以移除(向左倒下,此時向右倒下可以暫時不考慮),可以就移除,否則加入堆疊,直到檢查所有樹為止。

範例練習6    APCS202111 第3題生產線  貪婪+差分

有 n 台機器排成一直線, 每一個機器都有一個數值 t[i], 代表該台機器要產出一單位的資料需要 t[i] 單位的時間 接下來有 m 個工作要完成, 每一個工作都需要位置在 [l[i],r[i]] 的機器各生產出 w[i] 單位資料 現在你可以調換 n 台機器的順序, 目標是使得這 m 個工作做完的總時間要最小

輸入說明

先輸入兩個正整數 n 和 m 代表有 n 台機器和 m 個工作 接下來有 m 行, 每行有三個正整數 l[i], r[i] 和 w[i] 代表第 i 個工作需要編號從 l[i] 到 r[i] 的機器完成, 並且需要各產生出 w[i] 單位的資料 最後一行包含 n 個正整數 t[1],t[2],⋯t[n] 

數字範圍 

1≤n,m≤200000 

1≤w[i]≤100 

1≤t[i]≤100 

1≤l[i]≤r[i]≤n

輸出說明

輸出最小的總花費時間


輸入範例

5 1

2 4 1

1 2 3 4 5

10 3

2 5 6

3 6 4

7 8 1

1 2 3 4 5 6 7 8 9 10

輸出範例

6

117


解題策略

差分、排序、貪婪

第一組測資

5 1

2 4 1

1 2 3 4 5


生產資料量  1 1 1

時間           1 2 3 4 5

機器              1 2 3

          1*1+1*2+1*3=6


第二組測資

10 3

2 5 6

3 6 4

7 8 1

1 2 3 4 5 6 7 8 9 10



生產資料量     6 10 10 10  4  1  1

時間             1 2  3   4   5   6   7  8

機器                 4 1   2    3   5  6  7

          10*1 + 10*2 + 10*3 + 6*4 + 4*5 + 1*6 + 1*7 =117


使用差分與累加計算每一個時間點的生產資料量,生產資料量最大的時間點安排t[i]最小的機器,將生產資料量由大到小排序,t[i]由小到大排序,依序相乘累加。