# 貪婪演算法(greedy algorithm) 貪婪演算法,又稱貪心演算法 貪婪演算法在每一步決策中選擇當前看似最好的選項,目的在尋找**局部最優解**而非一定為**全局最優解** 給個例子,你有若干個1元、5元、10元硬幣,如果需要湊出16元,最少需要幾個硬幣? `coins = 0, cnt = 0` 優先選擇最大面額(10元) `coins = 1, cnt = 10` 10元會超出所求,選擇第二高面額(5元) `coins = 2, cnt = 15` 最後選擇1元 `coins = 3, cnt = 16` 故答為 `3` ## 例題 :::success ### [ntucpc- [Tutorial] 好吃的蛋糕](https://oj.ntucpc.org/problems/97) ::: :::spoiler 解題思路 將好吃度排序,並從最大開始加總$K$個,最後輸出 ::: :::spoiler WA 注意數字的大小 ::: :::spoiler AC Code 時間複雜度$O(N\ log\ N)$ ```cpp= #include <iostream> #include <algorithm> #include <vector> using namespace std; #define ll long long vector<ll> v; int main(){ ll n, k; cin >> n >> k; for(ll i = 0, tmp; i < n; i++){ cin >> tmp; v.push_back(tmp); } sort(v.begin(), v.end()); //O(log N) ll ans = 0; for(int i = n-1; i >= n-k; i--){ ans += v[i]; } cout << ans; return 0; } ``` ::: :::success ### [ntucpc- [Tutorial] 趕不完的作業](https://oj.ntucpc.org/problems/98) ::: :::spoiler 解題思路 一樣排序,但要考慮作業的截止時間,並且要在寫完一份作業後改變當前時間 ::: :::spoiler AC Code 時間複雜度$O(N\ log\ N)$ ```cpp= #include <iostream> #include <algorithm> #include <vector> using namespace std; #define ll long long vector<ll> v; int main(){ ll n, k; cin >> n; for(ll i = 0, tmp; i < n; i++){ cin >> tmp; v.push_back(tmp); } sort(v.begin(), v.end()); //O(log N) ll ans = 0, t = 0; for(int i = 0; i < n; i++){ if(v[i] >= t + 1){ //截止前1小時要完成 t++; ans++; } } cout << ans; return 0; } ``` ::: :::warning ### [ntucpc- [Tutorial] 趕不完的作業2](https://oj.ntucpc.org/problems/99) ::: :::spoiler 解題思路 記得「模擬」寫作業過程經過的時間,並且考慮做一份作業後接下來還可以做哪些 ::: :::spoiler AC Code 時間複雜度$O(N\ log\ N)$ ```cpp= #include <iostream> #include <vector> #include <algorithm> using namespace std; int main(){ int n, l; cin >> n >> l; vector<int> v(n); for(auto &i : v) cin >> i; sort(v.begin(), v.end()); int t = 0, ans = 0; for(auto i : v){ if(i < l || i-t < l) continue; t += l; ans++; } cout << ans << "\n"; return 0; } ``` ::: :::warning ### [ntucpc- 1072 . A.誰先晚餐](https://tioj.ck.tp.edu.tw/problems/1072) ::: :::spoiler 解題思路 每個人吃完一份餐的速度必為$t+C_i+E_i$分鐘,因此邊計算$max$邊模擬做菜過程即可 ::: :::spoiler tips 「多組測試資料」 ::: :::spoiler AC Code 時間複雜度$O(N\ log\ N)$ ```cpp= #include <iostream> #include <vector> #include <algorithm> using namespace std; int main(){ int n; while(true){ cin >> n; if(n == 0) break; vector<pair<int, int>> v; //{E, C} for(int i = 0; i < n; i++){ int c, e; cin >> c >> e; v.emplace_back(e, c); } sort(v.begin(), v.end()); reverse(v.begin(), v.end()); int t = 0, mx = -1e9; for(auto i : v){ auto [e, c] = i; mx = max(mx, t + c + e); t += c; } cout << mx << "\n"; } return 0; } ``` ::: :::warning ### [ntucpc- 713 . 餐點順序](https://oj.ntucpc.org/problems/713) ::: :::spoiler 解題思路 實際推演後可以發現到,||先做花費時間少的為局部最優解|| ::: :::spoiler AC Code 時間複雜度$O(N\ log\ N)$ ```cpp= #include <iostream> #include <vector> #include <algorithm> using namespace std; int main(){ long long n; cin >> n; vector<long long> v(n); for(auto &i : v) cin >> i; sort(v.begin(), v.end()); long long p = n; //still waiting long long ans = 0; for(auto i : v){ ans += (long long)(i * p); p--; } cout << ans << "\n"; return 0; } ``` ::: :::info ### <font color = "#9894F9"> 武陵高中114學科能競校內初選- pF Restaurants Around WLSH </font> (沒有連結只是好看) 此題無judge,公開測資對則正確 ###### ~~小小碎碎念:這題會留著是因為我打完這些說明後發現沒有judge,但我打很久所以留下來了~~ ::: #### **Description**: :::spoiler Description 零換粥是一名五零高中的學生,有一天晚上他留下來和同學吃飯,正當他們踏出校門時,他們遇到了世紀三大謎題之一———「晚餐要吃什麼」。身為五零老屁股,零換粥拿出了珍藏的美食餐廳清單,上面依序寫了他對每間餐廳的喜好度排名。為了不要白跑一趟,他們打算先查好每間餐廳有沒有開,再去喜好度排名最前面(數字最小)的餐廳。已知每間餐廳分別被編號為$1 ∼ N$ ,現在給你這份美食餐廳清單,以及每間餐廳的營業狀況,請你判斷他們應該要去哪間餐廳吃飯。 ::: #### **Input** :::spoiler Input 輸入的第一行是一個正整數 $N$,代表餐廳的數量。第二行有$N$ 個正整數,其中第 $i$ 個整數 $x_i$ 代表第 $i$ 間餐廳的喜好度排名。第三行有 $N$ 個正整數,皆為 $1$ 或 $0$ ,其中第 $i$ 個整數 $y_i$ 代表第 $i$ 間餐廳的營業狀況($1$ 代表有營業,$0$ 代表沒有營業)。 * $1 ≤ N ≤ 10^6$ * 對於所有$1 ≤ i ≤ N,1 ≤ x_i ≤ N$ ,保證$x_i$ 互不相同。 * 對於所有$1 ≤ i ≤ N,yi ∈ \{0, 1\}$ ,保證至少有一個$y_i$ 為 $1$。 ::: #### **Output** :::spoiler Output 輸出一個整數,代表他們最後選擇的餐廳的編號(亦即所有有營業的餐廳中,排名最前面的餐廳的編號)。 ::: #### **Samples** :::spoiler Samples | Sample No. | Input | Output | |:---------- |:---------------------------------------------------------------------------- |:------ | | 1 | 3 <br> 2 3 1 <br> 0 1 0 | 2 | | 2 | 10 <br> 1 2 3 4 5 6 7 8 9 10 <br> 1 1 1 1 1 1 1 1 1 1 | 1 | | 3 | 15 <br>9 15 4 10 13 6 2 12 5 8 7 1 14 3 11 <br>0 1 0 0 1 0 0 0 0 0 0 0 1 0 1 | 15 | ::: :::spoiler AC Code ||(by GDD) orzz|| ```cpp= #include <iostream> using namespace std; #define ll long long; ll N; ll A[1000010]; int main(){ cin >> N; for(int i = 1; i <= N; i++) cin >> A[i]; ll idx = -1; for(ll i = 1, v; i <= N; i++){ cin >> v; if(v){ if(idx == -1) idx = i; else{ if(A[i] < A[idx]) idx = i; } } } cout << idx << '\n'; return 0; } ``` ::: :::danger ### [CSES- Tasks and Deadlines](https://cses.fi/problemset/task/1630/) ::: #### 題目敘述翻譯 你有 $n$ 個任務要處理,且每個任務都須完成不能不做。 每個任務都有持續時間 $a$ 和截止日期 $d$。 每完成一個任務,會獲得 $(\ d-完成時間f\ )$ 的收益,此數值有可能為負 如果你採取最優行動,你的最大收益總和是多少? :::spoiler 解題思路 對於局部最優解而言,選擇先做持續時間短的比做截止日期近的較優,類似「誰先晚餐」的解題思路,持續時間會影響到後續的幅度比日期多。 因此此題應先將資料按照持續時間由小排到大,再根據題目做運算 ###### ~~注意: 資料範圍~~~~ ::: :::spoiler AC code ```cpp= #include <iostream> #include <vector> #include <algorithm> using namespace std; int main(){ int n; cin >> n; vector<pair<int, int>> v; for(int i = 0; i < n; i++){ int a, d; cin >> a >> d; v.emplace_back(a, d); } sort(v.begin(), v.end()); long long t = 0, ans = 0; //10^6 * (2 * 10^5) for(auto i : v){ auto [a, d] = i; t += a; ans += (d - t); } cout << ans; return 0; } ``` ::: >[!NOTE]...待新增...