講師:陳曉璇
貪婪演算法介紹
每一步都找目前看起來最好的結果
// 要考慮清楚是否會對下一步的最佳解造成影響
舉例來說……
以剛剛的背包問題來看……
目標:得到最大利潤
每次最好的方法:找價值最大的物品
以剛剛的背包問題來看……
目標:得到最大利潤
每次最好的方法:找價值最大的物品
| 重量 | 價值 |
|---|---|
| 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 不一定所有情況都能得到整體最佳解
目標:得到最短印刷裝訂時長
每次最好的方法:因為印刷機較少,讓印刷時長較短的先
| 編號 | 印刷時長 | 裝訂時長 |
|---|---|---|
| 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
//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;
}
目標:得到最短印刷裝訂時長
每次最好的方法:讓印刷時長較長的先,因為這樣機器閒置時間會最短
假設目前有三個委員會,分別有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
目標:將輸入的數字拆成乘積最大的組合
每次最好的方法:將數字從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;
}