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
想法一
目標:得到最短印刷裝訂時長
每次最好的方法:因為印刷機較少,讓印刷時長較短的先
編號 | 印刷時長 | 裝訂時長 |
---|---|---|
1 | 10 | 9 |
2 | 9 | 8 |
BOOK1
BOOK2
9
8
10
9
總時長:28
最佳解:27
不是整體最佳解
想法二
目標:得到最短印刷裝訂時長
每次最好的方法:讓總時長較長的先
編號 | 印刷時長 | 裝訂時長 | 總時長 |
---|---|---|---|
1 | 10 | 9 | 19 |
2 | 9 | 8 | 17 |
BOOK1
BOOK2
最佳解:27
是整體最佳解,但……
10
9
9
8
總時長:27
想法二
目標:得到最短印刷裝訂時長
每次最好的方法:讓總時長較長的先
編號 | 印刷時長 | 裝訂時長 | 總時長 |
---|---|---|---|
1 | 18 | 1 | 19 |
2 | 9 | 8 | 17 |
BOOK1
BOOK2
18
1
9
8
總時長:35
想法二
目標:得到最短印刷裝訂時長
每次最好的方法:讓總時長較長的先
編號 | 印刷時長 | 裝訂時長 | 總時長 |
---|---|---|---|
1 | 18 | 1 | 19 |
2 | 9 | 8 | 17 |
BOOK1
BOOK2
透過反例確認貪婪準則並不正確
18
1
9
8
總時長:28
想法三
目標:得到最短印刷裝訂時長
每次最好的方法:讓裝訂時長較長的先
編號 | 印刷時長 | 裝訂時長 |
---|---|---|
1 | 10 | 9 |
2 | 9 | 8 |
BOOK1
BOOK2
最佳解:27
是整體最佳解,原因是?
10
9
9
8
總時長:27
想法三
目標:得到最短印刷裝訂時長
每次最好的方法:讓裝訂時長較長的先
編號 | 印刷時長 | 裝訂時長 |
---|---|---|
1 | 10 | 9 |
2 | 9 | 8 |
印刷
裝訂
最佳解:27
10
9
9
8
總時長:27
想法三
目標:得到最短印刷裝訂時長
每次最好的方法:讓裝訂時長較長的先
編號 | 印刷時長 | 裝訂時長 |
---|---|---|
1 | 10 | 9 |
2 | 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...開始拆,最後多的數從尾巴平均分回
8
2
以 8 舉例
和:
2
思考
目標:將輸入的數字拆成乘積最大的組合
每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回
8
2
3
以 8 舉例
和:
5
思考
目標:將輸入的數字拆成乘積最大的組合
每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回
8
2
3
4
以 8 舉例
和:
9
思考
目標:將輸入的數字拆成乘積最大的組合
每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回
8
2
3
1
以 8 舉例
和:
6
思考
目標:將輸入的數字拆成乘積最大的組合
每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回
8
2
3
1
1
以 8 舉例
和:
7
思考
目標:將輸入的數字拆成乘積最大的組合
每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回
8
2
3
2
1
以 8 舉例
和:
8
思考
目標:將輸入的數字拆成乘積最大的組合
每次最好的方法:將數字從2,3,4,5...開始拆,最後多的數從尾巴平均分回
8
3
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