Greedy & Dp實作

講師:陳曉璇

目錄 Index

Greedy

貪婪演算法介紹

概念

局部最佳解

每一步都找目前看起來最好的結果

// 要考慮清楚是否會對下一步的最佳解造成影響

舉例來說……

一定會得到整體最佳解嗎?

以剛剛的背包問題來看……

目標:得到最大利潤

每次最好的方法:找價值最大的物品

一定會得到整體最佳解嗎?

以剛剛的背包問題來看……

目標:得到最大利潤

每次最好的方法:找價值最大的物品

重量 價值
3 60
2 50
4 40
6 70

最佳解

Greedy

重量:

價值:

重量:3+2+4

價值:150

一定會得到整體最佳解嗎?

以剛剛的背包問題來看……

目標:得到最大利潤

每次最好的方法:找價值最大的物品

重量 價值
6 70
3 60
5 50
4 40

最佳解

Greedy

重量:

價值:

重量:3+2+4

價值:150

一定會得到整體最佳解嗎?

以剛剛的背包問題來看……

目標:得到最大利潤

每次最好的方法:找價值最大的物品

重量 價值
6 70
3 60
2 50
4 40

最佳解

Greedy

重量:6+3

價值:70+60

重量:3+2+4

價值:150

不是整體最佳解

貪婪演算法小結

撰寫

1 定義貪婪準則(局部最佳解)

2 重複該準則直到解決問題為止

特色

1 簡單、省時

2 同個問題容易設計出多種解法

3 不一定所有情況都能得到整體最佳解

  • 計算結果擁有誤差(近似解)
  • 以合理成本取得近似解
  • 用於無法快速得到最佳解的問題

補充:近似演算法

Greedy實作1

想法一

目標:得到最短印刷裝訂時長

每次最好的方法:因為印刷機較少,讓印刷時長較短的先

編號 印刷時長 裝訂時長
10 9
9 8

BOOK

BOOK

9

8

10

9

總時長:28

最佳解:27

不是整體最佳解

想法二

目標:得到最短印刷裝訂時長

每次最好的方法:讓總時長較長的先

編號 印刷時長 裝訂時長 總時長
10 9 19
9 8 17

BOOK

BOOK

最佳解:27

是整體最佳解,但……

10

9

9

8

總時長:27

想法二

目標:得到最短印刷裝訂時長

每次最好的方法:讓總時長較長的先

編號 印刷時長 裝訂時長 總時長
18 1 19
9 8 17

BOOK

BOOK

18

1

9

8

總時長:35

想法二

目標:得到最短印刷裝訂時長

每次最好的方法:讓總時長較長的先

編號 印刷時長 裝訂時長 總時長
18 1 19
9 8 17

BOOK

BOOK

透過反例確認貪婪準則並不正確

18

1

9

8

總時長:28

想法三

目標:得到最短印刷裝訂時長

每次最好的方法:讓裝訂時長較長的先

編號 印刷時長 裝訂時長
10 9
9 8

BOOK

BOOK

最佳解:27

是整體最佳解,原因是?

10

9

9

8

總時長:27

想法三

目標:得到最短印刷裝訂時長

每次最好的方法:讓裝訂時長較長的先

編號 印刷時長 裝訂時長
10 9
9 8

印刷

裝訂

最佳解:27

10

9

9

8

總時長:27

想法三

目標:得到最短印刷裝訂時長

每次最好的方法:讓裝訂時長較長的先

編號 印刷時長 裝訂時長
10 9
9 8

印刷

裝訂

最佳解:27

機器閒置時間最短

總時長為累計印刷時間+最後完成裝訂的時間

10

9

9

8

總時長:28

code

//b231.book
#include <iostream>
#include<algorithm>
using namespace std;

struct Time{
    int print;
    int bind;
};

bool compare(Time a, Time b){
    return a.bind > b.bind;
}

int main()
{
    Time book[1005]={0};
    int n;
    
    while(cin >> n){
        for(int i=0;i<n;i++){
            cin >> book[i].print >> book[i].bind;
        }
        sort(book,book+n,compare);
        
        int sum=0,finalbind=0;
        for(int i=0;i<n;i++){
            sum+=book[i].print;
            finalbind=finalbind-book[i].print;
            if(book[i].bind>finalbind) finalbind=book[i].bind;
        }
        sum+=finalbind;
        cout << sum << endl;
    }
    return 0;
}

目標:得到最短印刷裝訂時長

每次最好的方法:讓印刷時長較長的先,因為這樣機器閒置時間會最短

Greedy實作

思考

假設目前有三個委員會,分別有2 4 6人,共有幾種組合?

2種組合

4種組合

6種組合

Ans:  2×4×6 = 32 種組合

目標:將輸入的數字拆成乘積最大的組合

思考

目標:將輸入的數字拆成乘積最大的組合

每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回

2

以 8 舉例

和:

2

思考

目標:將輸入的數字拆成乘積最大的組合

每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回

2

3

以 8 舉例

和:

5

思考

目標:將輸入的數字拆成乘積最大的組合

每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回

2

3

4

以 8 舉例

和:

9

思考

目標:將輸入的數字拆成乘積最大的組合

每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回

2

3

以 8 舉例

和:

6

思考

目標:將輸入的數字拆成乘積最大的組合

每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回

2

3

以 8 舉例

和:

7

思考

目標:將輸入的數字拆成乘積最大的組合

每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回

2

3

以 8 舉例

和:

8

思考

目標:將輸入的數字拆成乘積最大的組合

每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回

以 8 舉例

和:

8

CODE

目標:將輸入的數字拆成乘積最大的組合

每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回

#include <iostream>
using namespace std;

int main()
{
    int n,mem;
    cin >> n;
    while(n--){
        cin >> mem;
        int ans[500]={0},pos=0,sum=0;
        
        for(int i=2;sum+i<=mem;i++){
            ans[pos++]=i;
            sum+=i;
        }pos--;
        
        for(int i=0;sum++<mem;i++){
            if(pos-i<0) i=0;
            ans[pos-i]++;
        }
        
        for(int i=0;i<=pos;i++){
            cout << ans[i] << ' ';
        }cout << endl;
    }

    return 0;
}

參考資料

Greedy

By shaunna163

Greedy

  • 82