藍橋杯備賽(六)貪心算法

藍橋杯備賽(六)貪心算法

貪心的意義:(1)最終取得最優值
(2)比較短視,只看眼前的利益

  1. Acwing 1055. 股票買賣 II
    給定一個長度爲 N的數組,數組中的第 i個數字表示一個給定股票在第 i天的價格。
    設計一個算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。
    注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
    輸入格式
    第一行包含整數 N,表示數組長度。
    第二行包含 N 個不大於 10000的正整數,表示完整的數組。
    輸出格式
    輸出一個整數,表示最大利潤。
    數據範圍
    1≤N≤105
    輸入樣例1:
    6
    7 1 5 3 6 4
    輸出樣例1:
    7
    輸入樣例2:
    5
    1 2 3 4 5
    輸出樣例2:
    4
    輸入樣例3:
    5
    7 6 4 3 1
    輸出樣例3:
    0
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=100010;
int n,res;
int a[N];

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d",&a[i]);

    for(int i=0;i<n-1;i++)
    {
        int dt=a[i+1]-a[i];
        if(dt>0)     //貪心算法,只要後一個比前一個的貴,馬上賣掉
            res=res+dt;
    }
    cout<<res;
    getchar();getchar();
    return 0;
}
  1. Acwing 104. 貨倉選址
    在一條數軸上有 NN 家商店,它們的座標分別爲 A1A1~ANAN。
    現在需要在數軸上建立一家貨倉,每天清晨,從貨倉到每家商店都要運送一車商品。
    爲了提高效率,求把貨倉建在何處,可以使得貨倉到每家商店的距離之和最小。
    輸入格式
    第一行輸入整數N。
    第二行N個整數A1A1~ANAN。
    輸出格式
    輸出一個整數,表示距離之和的最小值。
    數據範圍
    1≤N≤1000001≤N≤100000
    輸入樣例:
    4
    6 2 9 1
    輸出樣例:
    12
    在這裏插入圖片描述
    在這裏插入圖片描述
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=100010;
int n;
long long res;
int a[N];

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    sort(a,a+n);

    int c=a[n/2]; //猜想。倉庫在中間的點時距離最小
    for(int i=0;i<n;i++)
        res=res+abs(a[i]-c);
    cout<<res;
    getchar();getchar();
    return 0;
}
  1. Acwing 122. 糖果傳遞
    有n個小朋友坐成一圈,每人有a[i]個糖果。
    每人只能給左右兩人傳遞糖果。
    每人每次傳遞一個糖果代價爲1。
    求使所有人獲得均等糖果的最小代價。
    輸入格式
    第一行輸入一個正整數n,表示小朋友的個數。
    接下來n行,每行一個整數a[i],表示第i個小朋友初始得到的糖果的顆數。
    輸出格式
    輸出一個整數,表示最小代價。
    數據範圍
    1≤n≤1000000
    數據保證一定有解。
    輸入樣例:
    4
    1
    2
    5
    4
    輸出樣例:
    4
    在這裏插入圖片描述
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1000010;
int n;
int a[N];
long long c[N],sum,avg;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    
    for(int i=1;i<=n;i++) sum+=a[i];
    avg=sum/n;
    for(int i=n;i>1;i--) 
        c[i]=c[i+1]+avg-a[i];
    c[1]=0;
    sort(c+1,c+n+1);

    long long res=0;
    for(int i=1;i<=n;i++) res+=abs(c[i]-c[(n+1)/2]);
    cout<<res;

    getchar();getchar();
    return 0;
}
  1. Acwing 112. 雷達設備
    假設海岸是一條無限長的直線,陸地位於海岸的一側,海洋位於另外一側。
    每個小島都位於海洋一側的某個點上。
    雷達裝置均位於海岸線上,且雷達的監測範圍爲d,當小島與某雷達的距離不超過d時,該小島可以被雷達覆蓋。
    我們使用笛卡爾座標系,定義海岸線爲x軸,海的一側在x軸上方,陸地一側在x軸下方。
    現在給出每個小島的具體座標以及雷達的檢測範圍,請你求出能夠使所有小島都被雷達覆蓋所需的最小雷達數目。
    輸入格式
    第一行輸入兩個整數n和d,分別代表小島數目和雷達檢測範圍。
    接下來n行,每行輸入兩個整數,分別代表小島的x,y軸座標。
    同一行數據之間用空格隔開。
    輸出格式
    輸出一個整數,代表所需的最小雷達數目,若沒有解決方案則所需數目輸出“-1”。
    數據範圍
    1≤n≤10001≤n≤1000
    輸入樣例:
    3 2
    1 2
    -3 1
    2 1
    輸出樣例:
    2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int N=1010;
int n,d;
struct Segment
{
    double l,r;
    bool operator < (const Segment& t) const
    {
        return r<t.r;
    }
}seg[N];
int main()
{
    cin>>n>>d;
    bool failed=false;
    for(int i=0;i<n;i++)
    {
        int x,y;
        cin>>x>>y;
        //將平面問題轉化到橫軸上的一個個區間
        if(y>d) failed=true;   //如果比半徑大,直接fail
        else
        {
            double len=sqrt(d*d-y*y);
            seg[i].l=x-len,seg[i].r=x+len; //區間左端點l,區間右端點r       
        }
    }
    if(failed)
        puts("-1");
    else
    {
        sort(seg,seg+n);
        int cnt=0;
        double last=-1e20;  //last表示上一個區間的右端點
        for(int i=0;i<n;i++)
        {
            if(last<seg[i].l)   
            {
                cnt++;  //若下一個區間左端點比上一個區間的右端點小,那麼就要再新加一個圓
                last=seg[i].r;  //更新
            }
        }
        cout<<cnt;
    }

    getchar();getchar();
    return 0;
}
  1. Acwing 1235. 付賬問題
    幾個人一起出去喫飯是常有的事。
    但在結帳的時候,常常會出現一些爭執。
    現在有 n個人出去喫飯,他們總共消費了 S元。
    其中第 i 個人帶了 ai元。
    幸運的是,所有人帶的錢的總數是足夠付賬的,但現在問題來了:每個人分別要出多少錢呢?
    爲了公平起見,我們希望在總付錢量恰好爲 SS 的前提下,最後每個人付的錢的標準差最小。
    這裏我們約定,每個人支付的錢數可以是任意非負實數,即可以不是 11 分錢的整數倍。
    你需要輸出最小的標準差是多少。
    標準差的介紹:標準差是多個數與它們平均數差值的平方平均數,一般用於刻畫這些數之間的“偏差有多大”。
    形式化地說,設第 ii 個人付的錢爲 bibi 元,那麼標準差爲 :

輸入格式
第一行包含兩個整數 n、Sn、S;
第二行包含 nn 個非負整數 a1, …, ana1, …, an。
輸出格式
輸出最小的標準差,四捨五入保留 4 位小數。
數據範圍
1≤n≤5×105
0≤ai,S≤109
輸入樣例1:
5 2333
666 666 666 666 666
輸出樣例1:
0.0000
輸入樣例2:
10 30
2 1 4 7 4 8 3 6 4 7
輸出樣例2:
0.7928

首先我們要知道標準差表示的是數據的波動程度,其值越大波動越大。要使得標準差小,我們就要儘可能使得數據都比較接近平均值。那麼這題貪心策略應該是這樣的:首先算出平均值s/n,把數據從小到大排序,如果某個人的錢低於該值,那麼他一定是將錢全部支付,然後其餘不夠的其他人平攤。但是,由於之前那個人錢不夠,那麼就會導致剩下人支付的平均值會增大,所以在這個平攤過程中很有可能存在某個人錢又低於這個平均值,又需要剩下的人平攤。如此反覆,直到支付完成。

#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int N= 500010;
int n;
double S;
int a[N];

int main() 
{
    cin>>n>>S;
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    sort(a,a+n);  //需要排序,若大的排到前面會浪費值

    double avg=S/n;   //均值
    double lavg =avg;  //剩餘部分平均值
    double res=S;  //剩餘要付的錢
    double r=0;  //標準差
    for(int i=0;i<n;i++)
    {
        if(a[i]<=lavg)  //如果手上的錢比均值小,全部出錢
        {
            r=r+(avg-a[i])*(avg-a[i]);
            res=res-a[i];
            lavg=res/(n-i-1); //更新剩餘部分的均值
        }
        else  //若比均值大,只出均值的錢
        {
            r=r+(avg-lavg)*(avg-lavg);
            res=res-lavg;
        }
    }
    printf("%.4lf",sqrt(r/n));
    getchar();getchar();
    return 0;
}
  1. Acwing 1239. 乘積最大
    給定 N 個整數 A1,A2,…AN。
    請你從中選出 K個數,使其乘積最大。
    請你求出最大的乘積,由於乘積可能超出整型範圍,你只需輸出乘積除以 1000000009 的餘數。
    注意,如果 X<0, 我們定義 X 除以 1000000009的餘數是負(−X)除以 1000000009的餘數,即:0−((0−x)%1000000009)
    輸入格式
    第一行包含兩個整數 N 和 K。
    以下 N行每行一個整數 Ai。
    輸出格式
    輸出一個整數,表示答案。
    數據範圍
    1≤K≤N≤105
    −105≤Ai≤105
    輸入樣例1:
    5 3
    -100000
    -10000
    2
    100000
    10000
    輸出樣例1:
    999100009
    輸入樣例2:
    5 3
    -100000
    -100000
    -2
    -100000
    -100000
    輸出樣例2:
    -999999829

在這裏插入圖片描述

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int N=100010,mod = 1000000009 ;
int a[N];

int main()
{
    int k,n;
    cin>>n>>k;
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    sort(a,a+n);

    long long res=1;  //乘積
    int l=0,r=n-1;    //雙指針
    int sign=1; //符號
    if(k%2)  //若k爲奇數,則需要先選出最大的正數
    {
        res=a[r];
        r--;
        k--;
        if(res<0) sign=-1; // 如果最大值都是負數,就證明全是負數,那麼符號要發生改變
    }
    while(k)
    {
        long long x=(long long)a[l]*a[l+1],y=(long long)a[r]*a[r-1];
        //從左右兩端一對一對選擇較大的
        if(x*sign>y*sign)
        {
            res=x%mod*res%mod;
            l+=2; //指針移動
        }
        else
        {
            res=y%mod*res%mod;
            r-=2;
        }
        k-=2;
    }
    cout<<res;
    getchar();getchar();
    return 0;
}
  1. Acwing 1247. 後綴表達式
    給定 N 個加號、M個減號以及 N+M+1 個整數 A1,A2,⋅⋅⋅,AN+M+1,小明想知道在所有由這 N 個加號、M個減號以及 N+M+1個整數湊出的合法的後綴表達式中,結果最大的是哪一個?
    請你輸出這個最大的結果。
    例如使用 123+−,則 “23+1−”這個後綴表達式結果是 4,是最大的。
    輸入格式
    第一行包含兩個整數 N 和 M。
    第二行包含 N+M+1個整數 A1,A2,⋅⋅⋅,AN+M+1。
    輸出格式
    輸出一個整數,代表答案。
    數據範圍
    0≤N,M≤105
    −109≤Ai≤109
    輸入樣例:
    1 1
    1 2 3
    輸出樣例:
    4
    在這裏插入圖片描述
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int N=200010;
int a[N],m,n;

int main()
{
    long long res=0;
    cin>>n>>m;
    for(int i=0;i<m+n+1;i++) scanf("%d",&a[i]);
    sort(a,a+m+n+1);

    if(!m)  //沒有負號,直接全加
    {
        for(int i=0;i<m+n+1;i++)
            res=res+a[i];
    }
    else //有負號
    {
        res=a[m+n]-a[0];  //一定有一個正數,一個負數,所以用最大值減最小值
        for(int i=1;i<m+n;i++)
            res=res+abs(a[i]);  //其他符號任選,全部選爲正
    }
    cout<<res;
    getchar();getchar();
    return 0;
}


第一部分完結撒花啦!!!
之後開始對前面所學的和題進行消化與刷題!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章