1. 程式人生 > >《算法競賽進階指南》0.7貪心

《算法競賽進階指南》0.7貪心

註意 find type 排列 for 單位 pair 初始 算法

110. 防曬

有C頭奶牛進行日光浴,第i頭奶牛需要minSPF[i]到maxSPF[i]單位強度之間的陽光。
每頭奶牛在日光浴前必須塗防曬霜,防曬霜有L種,塗上第i種之後,身體接收到的陽光強度就會穩定為SPF[i],第i種防曬霜有cover[i]瓶。
求最多可以滿足多少頭奶牛進行日光浴。

輸入格式
第一行輸入整數C和L。
接下來的C行,按次序每行輸入一頭牛的minSPF和maxSPF值,即第i行輸入minSPF[i]和maxSPF[i]。
再接下來的L行,按次序每行輸入一種防曬霜的SPF和cover值,即第i行輸入SPF[i]和cover[i]。
每行的數據之間用空格隔開。

輸出格式
輸出一個整數,代表最多可以滿足奶牛日光浴的奶牛數目。

數據範圍
1≤C,L≤2500,
1≤minSPF≤maxSPF≤1000,
1≤SPF≤1000

輸入樣例:
3 2
3 10
2 5
1 5
6 2
4 1

輸出樣例:
2

#include <iostream>
#include <algorithm>
#include <map>
#include <cstring>

using namespace std;

typedef pair<int, int> PII;//pair 易於排序

const int N = 2510;

int n, m;
PII cows[N];

int main()
{
    cin >> n >> m;
    for(int i = 0; i < n; i++) cin >> cows[i].first >> cows[i].second;
    sort(cows, cows + n);
    map<int, int> spfs; //用map 來儲存防曬霜
    for(int i = 0; i < m; i++) // 0開始沒有 <= 1開始才是
    {
        int spf, cover;
        cin >> spf >> cover;
        spfs[spf] += cover; // 註意這裏要寫 +=,因為數據中存在spf值相同的防曬霜
    }
    
    int res = 0; //初始化要賦值
    spfs[0] = spfs[1001] = n; //平衡樹兩個哨兵,方便找最大最小
    for(int i = n - 1; i >= 0; i--) // 從後往前遍歷前n個數 ,從大到小考慮
    {
        auto cow = cows[i]; //lower_bound >=某個數的最小值 upper_bound是>某個數的最小值
        auto it = spfs.upper_bound(cow.second);//auto 為 map<int, int>::iterator
        it --;
        if(it->first >= cow.first && it->first <= cow.second)
        {
            res ++;
            if(-- it->second == 0)
                spfs.erase(it);
        }
    }
    cout << res << endl;
    return 0;
}

111. 畜欄預定

有N頭牛在畜欄中吃草。

每個畜欄在同一時間段只能提供給一頭牛吃草,所以可能會需要多個畜欄。

給定N頭牛和每頭牛開始吃草的時間A以及結束吃草的時間B,每頭牛在[A,B]這一時間段內都會一直吃草。

當兩頭牛的吃草區間存在交集時(包括端點),這兩頭牛不能被安排在同一個畜欄吃草。

求需要的最小畜欄數目和每頭牛對應的畜欄方案。

輸入格式
第1行:輸入一個整數N。
第2..N+1行:第i+1行輸入第i頭牛的開始吃草時間A以及結束吃草時間B,數之間用空格隔開。

輸出格式
第1行:輸入一個整數,代表所需最小畜欄數。
第2..N+1行:第i+1行輸入第i頭牛被安排到的畜欄編號,編號從1開始,只要方案合法即可。

數據範圍
1≤N≤50000,
1≤A,B≤1000000

輸入樣例:
5
1 10
2 4
3 6
5 8
4 7

輸出樣例:
4
1
2
3
2
4

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

typedef pair<int, int> PII;
const int N = 50010;

int n;
pair<PII, int> cows[N]; //可以存三個,pair兩個,int一個 pair 自定有排序函數
int id[N];

int main()
{
    cin >> n;
    for(int i = 0; i < n; i++) 
    {
        cin >> cows[i].first.first >> cows[i].first.second;
        cows[i].second = i;  //cows[i].second為 id
    }
    sort(cows, cows + n);
    
    priority_queue<PII, vector<PII>, greater<PII>> heap; //小根堆定義
    
    for(int i = 0; i < n; i++)
    {
        auto cow = cows[i].first;
        if(heap.empty() || heap.top().first >= cow.first)
        {
            PII stall = {cow.second, heap.size() + 1};//畜欄數目,從1開始數,加1
            id[cows[i].second] = stall.second; //cows[i].second為 id
            heap.push(stall); //將當前畜欄加到堆裏面
        }
        else
        {
            auto stall = heap.top();
            heap.pop();
            stall.first = cow.second; // first存當前牛的結束吃草時間
            id[cows[i].second] = stall.second;
            heap.push(stall);
        }
    }
    cout << heap.size() << endl;
    for(int i = 0; i < n; i++) cout << id[i] <<endl;
    return 0;
}

112. 雷達設備

假設海岸是一條無限長的直線,陸地位於海岸的一側,海洋位於另外一側。

每個小島都位於海洋一側的某個點上。

雷達裝置均位於海岸線上,且雷達的監測範圍為d,當小島與某雷達的距離不超過d時,該小島可以被雷達覆蓋。

我們使用笛卡爾坐標系,定義海岸線為x軸,海的一側在x軸上方,陸地一側在x軸下方。

現在給出每個小島的具體坐標以及雷達的檢測範圍,請你求出能夠使所有小島都被雷達覆蓋所需的最小雷達數目。

輸入格式
第一行輸入兩個整數n和d,分別代表小島數目和雷達檢測範圍。
接下來n行,每行輸入兩個整數,分別代表小島的x,y軸坐標。
同一行數據之間用空格隔開。

輸出格式
輸出一個整數,代表所需的最小雷達數目,若沒有解決方案則所需數目輸出“-1”。

數據範圍
1≤n≤1000

輸入樣例:
3 2
1 2
-3 1
2 1

輸出樣例:
2

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

typedef pair<double, double> PDD;
const int N = 1010;
const double eps = 1e-6, INF = 1e10; // double一般定義一個eps精確值
 
int n, R;
PDD segs[N];

int main()
{
    bool success = true;
    
    cin >> n >> R;
    for(int i = 0; i < n; i++)
    {
        int x, y;
        cin >> x >> y;
        if(y > R)
        {
            success = false;
            break;
        }
        double len = sqrt(R * R - y * y);
        segs[i] = {x + len, x - len}; //區間 按終點起點排序
    }
    if(!success) puts("-1");
    else
    {
        sort(segs, segs + n);
        int res = 0;
        double last = -INF; //***這裏要定義double
        for(int i = 0; i < n; i++)
        {
            auto seg = segs[i];
            if(seg.second > last + eps) //seg.second 是起點
            {
                res ++;
                last = seg.first; // seg.first的終點
                
            }
        }
        cout << res << endl;
    }
    return 0;
}

114. 國王遊戲

恰逢 H 國國慶,國王邀請 n 位大臣來玩一個有獎遊戲。

首先,他讓每個大臣在左、右手上面分別寫下一個整數,國王自己也在左、右手上各寫一個整數。

然後,讓這 n 位大臣排成一排,國王站在隊伍的最前面。

排好隊後,所有的大臣都會獲得國王獎賞的若幹金幣,每位大臣獲得的金幣數分別是:

排在該大臣前面的所有人的左手上的數的乘積除以他自己右手上的數,然後向下取整得到的結果。

國王不希望某一個大臣獲得特別多的獎賞,所以他想請你幫他重新安排一下隊伍的順序,使得獲得獎賞最多的大臣,所獲獎賞盡可能的少。

註意,國王的位置始終在隊伍的最前面。

輸入格式
第一行包含一個整數 n,表示大臣的人數。
第二行包含兩個整數 a 和 b,之間用一個空格隔開,分別表示國王左手和右手上的整數。
接下來 n 行,每行包含兩個整數 a 和 b,之間用一個空格隔開,分別表示每個大臣左手和右手上的整數。

輸出格式
輸出只有一行,包含一個整數,表示重新排列後的隊伍中獲獎賞最多的大臣所獲得的金幣數。

數據範圍
1≤n≤1000
0<a,b<10000

輸入樣例:
3
1 1
2 3
7 4
4 6

輸出樣例:
2

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef pair<int, int>PII;
const int N = 1010;

int n;
PII ps[N];

vector<int> mul(vector<int> a, int b) //高精度乘法
{
    vector<int> c;
    int t = 0;
    for(int i = 0; i < a.size(); i++)
    {
        t += a[i] * b;
        c.push_back(t % 10); //取出余數
        t /= 10; //進位
    }
    while(t) c.push_back(t % 10), t /= 10;
    return c; 
}

vector<int> div(vector<int> a, int b)
{
    vector<int> c;
    bool is_first = false; // 避免輸出前面位的0
    for(int i = a.size() - 1, t = 0; i >= 0; i--) //從高位往低位
    {
        t = t * 10 + a[i]; //豎式後位的值
        int x = t / b;
        if(x || is_first)
        {
            is_first  = true;
            c.push_back(x);
        }
        t %= b;
    }
    return vector<int>(c.rbegin(), c.rend()); //逆序遍歷
}

vector<int> max_vec(vector<int>a, vector<int> b) // 返回vector
{
    if(a.size() > b.size()) return a;
    if(a.size() < b.size()) return b;
    if(vector<int>(a.rbegin(), a.rend()) > vector<int>(b.rbegin(), b.rend()))//構造新的vector,是a的逆序
        return a;
    return b;    
}

void output(vector<int> a)
{
    for(int i = a.size() - 1; i >= 0; i--) cout << a[i]; //高精度存,小位在前,高位在後,因為增加一位只能在後面增加
    cout << endl;
}

int main()
{
    cin >> n;
    for(int i = 0; i <= n; i++) // 0位國王,1...n+1為大臣
    {
        int a, b;
        cin >> a >> b;
        ps[i] = {a * b, a};
    }
    sort(ps + 1, ps + n + 1);
    
    vector<int>product(1, 1); //乘積長度為1,初始為1
    vector<int>res(1,0); // 結果長度為1,初始為0
    
    for(int i = 0; i <= n; i++)
    {
        if(i) res = max_vec(res, div(product, ps[i].first / ps[i].second));
        product = mul(product, ps[i].second);
    }
    
    output(res);
    return 0;
}

115. 給樹染色

一顆樹有 n 個節點,這些節點被標號為:1,2,3…n,每個節點 i 都有一個權值 A[i]。

現在要把這棵樹的節點全部染色,染色的規則是:

根節點R可以隨時被染色;對於其他節點,在被染色之前它的父親節點必須已經染上了色。

每次染色的代價為T*A[i],其中T代表當前是第幾次染色。

求把這棵樹染色的最小總代價。

輸入格式
第一行包含兩個整數 n 和 R ,分別代表樹的節點數以及根節點的序號。
第二行包含 n 個整數,代表所有節點的權值,第 i 個數即為第 i 個節點的權值 A[i]。
接下來n-1行,每行包含兩個整數 a 和 b ,代表兩個節點的序號,兩節點滿足關系: a 節點是 b 節點的父節點。
除根節點外的其他 n-1 個節點的父節點和它們本身會在這 n-1 行中表示出來。
同一行內的數用空格隔開。

輸出格式
輸出一個整數,代表把這棵樹染色的最小總代價。

數據範圍
1≤n≤1000,
1≤A[i]≤1000

輸入樣例:
5 1
1 2 1 2 4
1 2
1 3
2 4
3 5
0 0

輸出樣例:
33

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

struct Node
{
    int father, size, sum;
    double avg;
}nodes[N];

int n, root;

int find() //找權值最大的根節點
{
    double avg = 0;
    int res = -1;
    for(int i = 1; i <=n; i++)
        if(i != root && avg < nodes[i].avg)
        {
            avg = nodes[i].avg;
            res = i;
        }
    return res;    
}

int main()
{
    int res = 0;
    cin >> n >> root;
    for(int i = 1; i <= n; i++)
    {
        auto &nd = nodes[i];
        cin >> nd.sum;
        nd.size = 1;
        nd.avg = nd.sum;
        res += nd.sum; //初始賦值 每個數乘一加一塊,所有數的和
    }
    for(int i = 0, a, b; i < n - 1;i++)
    {
        cin >> a >> b;
        nodes[b].father = a;
    }
    
    for(int i = 0; i < n - 1; i++)
    {
        int ver = find();
        int f = nodes[ver].father;
        res += nodes[ver].sum * nodes[f].size;
        nodes[ver].avg = -1;
        for(int j = 1; j <= n; j++)
            if(nodes[j].father == ver)
                nodes[j].father = f;
        nodes[f].sum += nodes[ver].sum;
        nodes[f].size += nodes[ver].size;
        nodes[f].avg = (double)nodes[f].sum / nodes[f].size;
    }
    cout << res << endl;
    return 0;
}

《算法競賽進階指南》0.7貪心