[啃書] 第7篇 - 演算法初步(貪心)
阿新 • • 發佈:2021-06-23
前言
接著第四章
本篇總結自《演算法筆記》4.4 貪心
正文
知識點1:簡單貪心
貪心是隻考慮當下的一種區域性最優(或較優),來使全域性結果達到最優(或較優)的策略。
證明策略是最優(或較優)的:反證法、數學歸納法【證明較複雜,若有不錯的思路且無法找到反例,直接實現即可】
例1 PAT B1020(賣月餅P118)
#include<cstdio> #include<algorithm> using namespace std; struct mooncake { // 結構體整合輸入資料 double store; // 庫存 double sell; // 總價double price; // 單價(庫存除總價) }cake[1010]; bool cmp(mooncake a, mooncake b) { return a.price > b.price; } int main() { int n; // 月餅種類數 double D; // 月餅需求量 scanf("%d%lf", &n, &D); for(int i = 0; i < n; i++) { // 輸入庫存 scanf("%lf", &cake[i].store); } for(inti = 0; i < n; i++) { // 輸入總價 並計算單價 scanf("%lf", &cake[i].sell); cake[i].price = cake[i].sell / cake[i].store; } sort(cake, cake + n, cmp); // 按單價從高到低排序 double ans = 0; // 收益 for(int i = 0; i < n; i++) { // 遍歷排序後的月餅列表cake if(D >= cake[i].store) { // 需求大於供應 D -= cake[i].store; ans+= cake[i].sell; // 該類月餅全部賣出 } else { // 需求小於供應 ans += cake[i].price * D; // 需求全部滿足 break; } } printf("%.2f\n", ans); return 0; }
只在乎銷售額,只要每次選最貴的就能達到總銷售額最多(貪心)。
例2 PAT B1023(組個最小數)
#include<cstdio> int main() { int count[10]; // 記錄數字0~9的個數 for(int i = 0; i < 10; i++) { scanf("%d", count + i); // 錄入0~9各數字個數 } for(int i = 1; i < 10; i++) { if(count[i] > 0) { printf("%d", i); // 輸出除0外最小的數作為最高位 count[i]--; break; } } for(int i = 0; i < 10; i++) { for(int j = 0; j < count[i]; j++) { // 把i輸出count[i]次 printf("%d", i); } } return 0; }
只要每次都拿最小的數放在最高位,最後組成的數必然最小(貪心)。
知識點2:區間貪心(給出若干小區間,看最多能塞多少個)
#include<cstdio> #include<algorithm> using namespace std; const int maxn = 110; struct Interval{ int x, y; }I[maxn]; bool cmp(Interval a, Interval b) { if(a.x != b.x) return a.x > b.x; // 左端點從大到小 else return a.y < b.y; // 左端點相同時,右端點從小到大(寫b.y > a.y容易理解錯) } int main() { int n; while(scanf("%d", &n), n != 0) { // 輸入提供選擇的區間個數 for (int i = 0; i < n; i++) { scanf("%d%d", &I[i].x, &I[i].y); // 順序輸入區間的左右端點 } sort(I, I + n, cmp); // 把區間排序 int ans = 1, lastX = I[0].x; // ans記錄不相交區間數,lastX記錄上一個被選區間的左端點 for(int i = 1; i < n; i++) { if(I[i].y <= lastX) { // 若該區間右端點在lastX左邊 lastX = I[i].x; // 以I[i]作為新選中的區間 ans++; // 區間數++ } } printf("%d\n", ans); } return 0; }
稍微複雜的貪心策略:如下圖兩種情況按下標順序選取