蓝桥杯 第七讲 贪心

2022/2/13 23:45:02

本文主要是介绍蓝桥杯 第七讲 贪心,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、题目特点

  1. 跳跃性很强
  2. 结论证明很难

二、解题策略

  1. 找相似
  2. 猜想

AcWing 1055. 股票买卖 II

image

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

using namespace std;

const int N = 1e5 + 10;

int p[N],ans,n;

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &p[i]);
    }
    for (int i = 0; i < n-1; i ++ )
    {
        if(p[i+1] > p[i]) ans += p[i+1] - p[i];//贪心策略:只要比前一天价格高,就买入
    }
    cout << ans << endl;
    return 0;
}

AcWing 104. 货仓选址

中位数有非常优秀的性质,比如说在这道题目中,每一个点到中位数的距离,都是满足全局的最有性,而不是局部最优性。
具体的来说,我们设在仓库左边的所有点,到仓库的距离之和为p,右边的距离之和则为q,那么我们就必须让p+q的值尽量小。
当仓库向左移动的话,p会减少x,但是q会增加n−x,所以说当为仓库中位数的时候,p+q最小。
还是同样的一句话,画图理解很重要。

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

using namespace std;
typedef long long LL;

const int N = 1e5 + 10;

int a[N],n;
LL ans;

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &a[i]);
    }
    sort(a,a+n);
    for (int i = 0; i < n; i ++ )
    {
        ans += abs(a[n>>1] - a[i]);
    }
    cout << ans <<endl;
    return 0;
}

122. 糖果传递

image
image
image

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

using namespace std;
typedef long long LL;

const int N = 1e6 + 10;

int a[N],c[N];
int n;
LL ave,ans;

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &a[i]);
        ave += a[i];
    }
    ave /= n;
    for(int i = n;i>1;i--)
    {
        c[i] = c[i+1] + ave - a[i];
    }
    c[1] = 0;
    sort(c+1,c+n+1);
    for (int i = 1; i <= n; i ++ )
    {
        ans += abs(c[i] - c[(i+1)/2]);
    }
    cout << ans << endl;
    return 0;
}

112. 雷达设备

image

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

using namespace std;

const int N = 1010;

struct Range
{
    double l;
    double r;
    bool operator<(struct Range& t) const
    {
        return r < t.r;
    }
}range[N];

int n,d,x,y;

int main()
{
    scanf("%d%d", &n,&d);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d%d", &x, &y);
        if(y > d)  //如果y大于检测半径,则无法覆盖到
        {
            cout << -1 << endl;
            return 0;
        }
        double t = sqrt(d*d - y*y);  //安置能够监测到该小岛的雷达的范围,勾股定理
        range[i] = {x-t,x+t};
    }
    sort(range,range+n);  //区间选点思路做法,右端点排序
    int ans = 1;
    double ed = range[0].r;
    for (int i = 1; i < n; i ++ )
    {
        double l = range[i].l,r = range[i].r;
        if(l > ed) //若当前区间左端点比上一个选出的区间的左端点大,说明必须再拿一个点表示该区间
        {
            ++ans;
            ed = r;  
        }
        //否则的话就舍弃该区间
    }
    cout << ans << endl;
    return 0;
}

1235. 付账问题

image

升序排序后,均摊当前剩下的费用,带钱少的有多少付多少

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

using namespace std;
typedef long long LL;

const int N = 5e5 + 10;
int n;
double s;
int a[N];
double ave,ans;
int main()
{
    scanf("%d%lf", &n,&s);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &a[i]);
    }
    sort(a,a+n);  //升序排序
    ave = 1.0*s/n;  
    for (int i = 0; i < n; i ++ )
    {
        double cur = 1.0*s/(n-i);  //均摊当前剩余款项
        if(a[i] < cur) cur = a[i];  //钱不够的有多少付多少,钱够的付平均值
        ans += (cur - ave) * (cur - ave);
        s -= cur;
    }
    ans = 1.0*ans/n;
    printf("%.4lf\n",sqrt(ans));
    return 0;
}

1239. 乘积最大

C++中,正数取模正数是正数,负数取模正数是负数,取模的数的正负对结果无关
image

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

using namespace std;

const int N = 1e5 + 10,MOD = 1000000009;
typedef long long LL;

int n,k;
int a[N];

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &a[i]);
    }
    sort(a,a+n);
    LL ans = 1;
    int l = 0,r = n-1;
    int sign = 1;  //判断整个数组是否全为负数,是为-1,否则为1
    if(k%2 == 1)  //选奇数个数
    {
        ans = a[r--];  //此情况下,最大值必选
        --k;
        if(ans < 0) sign = -1;  //最大值是负数,整个数组全是负数
    }
    //如果选偶数个,成对选择乘积较大的即可
    while(k)
    {
        LL x = (LL)a[l] * a[l+1],y = (LL)a[r] * a[r-1];  //选取左右两组数
        if(x * sign < y * sign)  //如果全为负数,就要选绝对值较小的去乘,否则选绝对值较大的去乘
        {
            ans = y % MOD * ans % MOD; //一定要先取余,后相乘
            r-=2;
        }
        else
        {
            ans = x % MOD * ans % MOD; //一定要先取余,后相乘
            l+=2;
        }
        k-=2;
    }
    cout << ans << endl;
    return 0;
}

1247. 后缀表达式

image
image

逆波兰表达式可以通过"加括号"的方式调整运算顺序,即把负号变成正号,尽可能的给每个负数配一个负号,给每个正数配一个正号
至少减一个数,加一个数,贪心策略就是:减最小的,加最大的
一般情况:把所有数排个序,最大的拿出来,放首项,把最小的数拿出来,给他一个减号,再套一个括号,那么现在还未完成的表达式长这样:
image
可以发现,现在如果我想加一个数的话,给它一个加号,放在括号外面,也可以给它一个减号,放在括号里面;减一个数同理。换句话说,只要用一个减号,一个最大值,一个最小值,其他数我想加就加,想减就减。那么为了使结果最大,我加上正数,减去负数,就是直接加上所有剩下数的绝对值,那么就解决了。

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

using namespace std;

const int N = 1e5 + 10;
typedef long long LL;

int a[2*N],n,m;  //注意数的个数可以为两倍的N

int main()
{
    scanf("%d%d", &n, &m);
    int k = n + m + 1;
    for (int i = 0; i < k; i ++ )
    {
        scanf("%d", &a[i]);
    }
    LL ans = 0;
    if(m==0)  //没有减号,直接全加
    {
        for (int i = 0; i < k; i ++ )
        {
            ans += a[i];
        }
    }
    else  //有减号
    {
        sort(a,a+k);  //也可以不排序,直接找最值
        ans = a[k-1] - a[0];  // 贪心策略:加上最大值,减去最小值
        for (int i = 1; i < k-1; i ++ )  //其余的数想加就加,想减就减,贪心策略:负数就减,正数就加
        {
            ans += abs(a[i]);
        }
    }
    cout << ans << endl;
    return 0;
}

1248. 灵能传输

image
image

a[i] = s[i] - s[i-1]与题意,要让a[i]的绝对值尽可能小,就要让前缀和数组s两两之间的差的绝对值尽可能小
贪心策略:对前缀和数组进行冒泡排序

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

using namespace std;
typedef long long LL;

const int N = 300005;
int T;
LL a[N],s[N],n;
bool st[N];
int main()
{
    scanf("%d", &T);
    while (T -- )
    {
        scanf("%d", &n);
        s[0] = 0;
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%lld", &a[i]);
            s[i] = s[i-1] + a[i];
        }
        LL s0 = s[0],sn = s[n];
        if(s0 > sn) swap(s0,sn);
        sort(s,s+n+1);
        
        for (int i = 0; i < n; i ++ )
        {
            if(s[i] == s0)
            {
                s0 = i;
                break;
            }
        }
        for (int i = n; i >= 0; i -- )
        {
            if(s[i] == sn)
            {
                sn = i;
                break;
            }
        }
        
        memset(st, 0, sizeof st);
        int l = 0,r = n;
        for(int i = s0;i>=0;i-=2)
        {
            a[l++] = s[i];
            st[i] = true;
        }
        for(int i = sn;i<=n;i+=2)
        {
            a[r--] = s[i];
            st[i] = true;
        }
        for (int i = 0; i <= n; i ++ )
        {
            if(!st[i])
            {
                a[l++] = s[i];
            }
        }
        LL res = 0;
        for (int i = 1; i <= n; i ++ ) res = max(res,abs(a[i] - a[i-1]));
        cout<<res<<endl;
    }
    return 0;
}


这篇关于蓝桥杯 第七讲 贪心的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程