算法進階指南0x 貪心

防曬

有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

貪心決策:將每頭牛按MINspf從大到小排序,每頭牛選剛好滿足條件且最大的SPF,證明請參考lyd大佬。。。。

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

#define pii pair<int,int>

using namespace std;

const int N=2510;

map<int,int> mp;
pii cows[N];
int n,m,res;

int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    
    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;i++)
        scanf("%d%d",&cows[i].first,&cows[i].second);

    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        mp[x]+=y;
    }

    sort(cows+1,cows+n+1);

    for(int i=n;i>=1;i--)
    {
        auto it=mp.upper_bound(cows[i].second);
        it--;
        if(it->first>=cows[i].first&&it->first<=cows[i].second)
        {
            res++;
            if(--(it->second)==0)
                mp.erase(it);
        }
    }
    cout<<res<<endl;

    return 0;
}

畜欄預定

有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<algorithm>
#include<cstring>
#include<queue>

#define pii pair<int,int>

using namespace std;

const int N=50010;

struct Cows{
    int l,r,num;
};

bool CMP(const Cows &a,const Cows &b)
{
    return a.l<b.l;
}

int n;
Cows cows[N];
int id[N];

int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    
    scanf("%d",&n);

    for(int i=1;i<=n;i++)
        scanf("%d%d",&cows[i].l,&cows[i].r),cows[i].num=i;

    sort(cows+1,cows+n+1,CMP);

    priority_queue<pii,vector<pii>,greater<pii>> heap;

    for(int i=1;i<=n;i++)
    {
        if(heap.empty() || heap.top().first>=cows[i].l)
        {
            heap.push({cows[i].r,heap.size()+1});
            id[cows[i].num]=heap.size();
        }
        else
        {
            auto temp=heap.top();
            heap.pop();
            heap.push({cows[i].r,temp.second});
            id[cows[i].num]=temp.second;
        }
        
    }
    printf("%d\n",heap.size());
    for(int i=1;i<=n;i++)
        printf("%d\n",id[i]);

    return 0;
}

雷達設備

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

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

雷達裝置均位於海岸線上,且雷達的監測範圍爲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<cstring>
#include<algorithm>
#include<cmath>

#define pdd pair<double,double>

using namespace std;

const int N=1010;
const double eps=1e-6,INF=1e10;

pdd point[N];
int n,r;

bool CMP(const pdd &a,const pdd &b)
{
    return a.second<b.second;
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    
    scanf("%d%d",&n,&r);

    bool flag=false;
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(abs(y)>r)
        {
            flag=true;
            break;
        }
        double d=sqrt(1.0*r*r-y*y);
        point[i]={x-d,x+d};
    }

    if(flag) puts("-1");
    else 
    {
        sort(point+1,point+n+1,CMP);
        double last=-INF;
        int res=0;
        for(int i=1;i<=n;i++)
        {
            if(point[i].first>last+eps)
            {
                last=point[i].second;
                res++;
            }
        }
        printf("%d\n",res);
    }
    return 0;
}

國王遊戲

恰逢 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<cstring>
#include<algorithm>
#include<vector>

#define pii pair<int,int>

using namespace std;

const int N=1010;

pii att[N];
int n;

vector<int> mul(vector<int> &a,int b)
{
    vector<int> res;
    int t=0;
    for(int i=0;i<a.size();i++)
    {
        t+=a[i]*b;
        res.push_back(t%10);
        t/=10;
    }
    while(t)
    {
        res.push_back(t%10);
        t/=10;
    }
    return res;
}

vector<int> div(vector<int> &a,int b)
{
    vector<int> res;
    int t=0;
    for(int i=a.size()-1;i>=0;i--)
    {
        t=10*t+a[i];
        res.push_back(t/b);
        t%=b;
    }

    reverse(res.begin(),res.end());
    while(res.size()>1&&res.back()==0) res.pop_back();
    return res;
}

bool compare(const vector<int> &a,const vector<int> &b){
    if(a.size()!=b.size()) return a.size()>b.size();
    return vector<int> (a.rbegin(),a.rend()) > vector<int> (b.rbegin(),b.rend());

}

bool CMP(const pii &a,const pii &b)
{
    return a.first*a.second<b.first*b.second;
}

int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);

    scanf("%d",&n);

    for(int i=0;i<=n;i++)
        scanf("%d%d",&att[i].first,&att[i].second);

    sort(att+1,att+n+1,CMP);

    vector<int> res(1,0);
    vector<int> temp(1,1);

    temp=mul(temp,att[0].first);

    for(int i=1;i<=n;i++)
    {
        vector<int> value=div(temp,att[i].second);
        if(!compare(res,value)) res=value;
        temp=mul(temp,att[i].first);
    }
    reverse(res.begin(),res.end());
    for(auto x:res) cout<<x;
    puts("");

    return 0;
}

給樹染色

一顆樹有 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
輸出樣例:
33

這題的貪心思路就是:染色順序按每個點及其子節點的平均值進行排序,但我們不會傻呵呵的每次更新都算一下平均值,高斯告訴我們:做任何事都要找竅門,我們不斷的歸併節點,最後全部轉移到根節點上,這樣我們就可以省略了求平均值的過程,這個做法爲什麼是對的呢?請自行上網搜索,主要是不會證明

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

using namespace std;

const int N=1010;

struct Node
{
    int w,s,p;
    double avg;
};

Node node[N];
int n,root;

int get(){
    int pos=-1;
    double maxv=-1;
    for(int i=1;i<=n;i++)
    {
        if(i!=root&&maxv<node[i].avg)
        {
            pos=i;
            maxv=node[i].avg;
        }
    }

    return pos;
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    
    scanf("%d%d",&n,&root);

    for(int i=1;i<=n;i++)
    {
        scanf("%d",&node[i].w);
        node[i].avg=node[i].w;
        node[i].s=1;
    }

    for(int i=0;i<n-1;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        node[b].p=a;
    }
    
    int res=0;

    for(int i=0;i<n-1;i++)
    {
        int x=get();
        int fa=node[x].p;
        res+=node[x].w*node[fa].s;
        
        for(int j=1;j<=n;j++)
            if(node[j].p==x)
                node[j].p=fa;
        
        node[x].avg=-1;
        node[fa].w+=node[x].w;
        node[fa].s+=node[x].s;
        node[fa].avg=1.0*node[fa].w/node[fa].s;
    }
    res+=node[root].w;
    printf("%d\n",res);

    return 0;
}
發佈了29 篇原創文章 · 獲贊 38 · 訪問量 2701
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章