C++ 小社
貪婪演算法 Greedy Algorithm
演算法(Algorithm)是一組解決問題的明確指令或步驟,用來處理輸入資料、經過有限次數的運算,最終產生預期輸出;它像電腦的「食譜」,是電腦科學的基礎,存在於數學運算、資料處理、人工智慧等各領域,確保在有限時間和資源內達成目標,是現代科技中不可或缺的核心概念
。
- Gemini
演算法策略
貪婪法
分治法
回溯法
分支限界法
動態規劃法
演算法策略
貪婪法
分治法
回溯法
分支限界法
動態規劃法
在每一步選擇中,都採取當前狀態下「最好」或「最優」的選擇,而不考慮長遠後的影響。
將一個複雜的問題,拆解成若干個規模較小、結構相同的子問題,分別解決後再合併結果。(例:Merge Sort)
嘗試每一種可能的路徑,當發現這條路走不通(不符合條件)時,就退回上一步,改走別條路。
它會維護一個「界限」(Bound),在搜尋過程中,如果發現某個分支最好的可能結果也比不上目前已找到的解,就直接捨棄該分支。
當子問題會「重複出現」時,我們會將子問題的結果存起來(查表),下次遇到相同的子問題直接拿來用,避免重複計算。

貪婪演算法就是一個貪婪的演算法(?)
貪婪的定義:
在每一步尋找最佳解,以形成全域的最佳解
Step 1 定義貪婪準則
Step 2 不斷重複貪婪準則,直到解決問題為止
Knapsack problem
【背包問題】
有一個背包可以背特定重量的東西,
有一堆東西,
每個東西有自己的重量和價值。
要選哪些東西裝進背包,才能最大化所背的價值?
Knapsack problem
| 物品編號 | 重量 | 價值 |
|---|---|---|
| 0 | 22 | 19 |
| 1 | 10 | 9 |
| 2 | 9 | 9 |
| 3 | 7 | 6 |
背包可承受重量 = 25
Fractional Knapsack problem
【可以分割的背包問題】
拿一個東西不一定要整個都拿,可以拿一部分就好。
Fractional Knapsack problem
如果從輕的開始拿 : 6 + 9 + 9*(9/10) =23.1
| 物品編號 | 重量 | 價值 |
|---|---|---|
| 0 | 22 | 19 |
| 1 | 10 | 9 |
| 2 | 9 | 9 |
| 3 | 7 | 6 |
Fractional Knapsack problem
如果從貴的開始拿 :19 + 9*(3/10) = 21.7
| 物品編號 | 重量 | 價值 |
|---|---|---|
| 0 | 22 | 19 |
| 1 | 10 | 9 |
| 2 | 9 | 9 |
| 3 | 7 | 6 |
Fractional Knapsack problem
好像怪怪的?
應該把價錢和重量都考量進去
Fractional Knapsack problem
| 物品編號 | 重量 | 價值 | 價值 / 重量 |
|---|---|---|---|
| 0 | 22 | 19 | 0.8636 |
| 1 | 10 | 9 | 0.9 |
| 2 | 9 | 9 | 1 |
| 3 | 7 | 6 | 0.857 |
9+9+19*(6/22) = 23.18
貪婪:每一步都選擇「性價比最高」的選項
#include<iostream>
#include<algorithm>
using namespace std;
struct obj{
int w;
int v;
double vw;
};
bool cmp(obj a,obj b){
return (a.vw>b.vw);
}
int main(){
int n,k,ktmp;
double totalv;
obj ob[101];
while(cin>>n){
for(int i=0;i<n;i++){
cin >> ob[i].w >> ob[i].v;
ob[i].vw=(double)ob[i].v/ob[i].w;
}
cin >> k;
ktmp=k;
sort(ob,ob+n,cmp);
for(int i=0;i<n;i++){
if (ob[i].w<=ktmp) {
totalv+=ob[i].v;
ktmp-=ob[i].w;
}else{
totalv+=(double)ob[i].vw*ktmp;
break;
}
}
cout << totalv << endl;
}
}最多有幾個工作可以執行
有 n 個工作可以執行,給定每個工作的開始時間與結束時間,時間從 0 開始,開始與結束時間都是整數。
只有一台機器可以執行,每次只能執行一個工作,且工作開始做就需要做完。
不考慮切換所需時間,請計算執行完後最多有幾個工作被完成?
貪婪準則是將結束時間最早的工作先做,讓機器可以空下來準備做其他工作。先將n個工作以結束時間較早的工作排在前面,利用此原則排序好n個工作。
#include<iostream>
#include<algorithm>
using namespace std;
struct job{
int s;
int e;
};
bool cmp(job a,job b){
return a.e<b.e;
}
int main(){
int n,now,ans;
job jb[101];
while(cin>>n){
for(int i=0;i<n;i++){
cin >> jb[i].s>>jb[i].e;
}
sort(jb,jb+n,cmp);
ans=0;
now=-1;
for(int i=0;i<n;i++){
if (now<=jb[i].s) {
ans++;
now=jb[i].e;
}
}
cout << ans << endl;
}
}最多有幾台機器一起運作
有n個工作可以執行,給定每個工作的開始時間與結束時間,工作時間可能重疊。
時間從0開始,開始與結束時間都是整數,有n台機器可以執行,每台機器同時間只可以執行一個工作,工作可以到每台機器去執行,且工作開始做就需要做完,機器執行中不能跳到另一個工作,可以一結束就馬上接著執行另一個工作,機器更換工作很快,可以不考慮切換所需時間,執行完後最少需要幾台機器才能完成所有工作?
最多有幾台機器一起運作
貪婪準則是先將n個工作以開始時間最早的工作排在前面,若開始時間相同就以最早結束的工作排在前面進行排序,將排序好的工作,由最前面依序取出每個工作,優先分配到目前已經執行完畢或沒有工作可以執行的機器,若全部機器都有工作正在執行就需要啟用新的機器。需使用陣列m,紀錄每個機器執行目前工作完成後的時間,才能判斷機器是否有空可以執行下一個工作,還是要啟動新的機器。
#include<iostream>
#include<algorithm>
using namespace std;
struct job{
int s;
int e;
};
bool cmp(job a,job b){
if (a.s==b.s) return a.e<b.e;
return a.s<b.s;
}
int main(){
int n,ans,m[101];
job jb[101];
while(cin>>n){
for(int i=0;i<n;i++){
cin >> jb[i].s>>jb[i].e;
}
sort(jb,jb+n,cmp);
ans=1;
m[0]=jb[0].e;
for(int i=1;i<n;i++){
bool found=false;
for(int j=0;j<ans;j++){
if (m[j]<=jb[i].s){
m[j]=jb[i].e;
found=true;
break;
}
}
if (!found){
m[ans]=jb[i].e;
ans++;
}
}
cout << ans << endl;
}
}C++ 小社 貪婪
By Suzy Huang
C++ 小社 貪婪
- 65