1. 程式人生 > 其它 >[啃書] 第7篇 - 演算法初步(貪心)

[啃書] 第7篇 - 演算法初步(貪心)

前言

接著第四章

本篇總結自《演算法筆記》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(int
i = 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;
}

稍微複雜的貪心策略:如下圖兩種情況按下標順序選取