贪心例题

1.雷达设备

题目链接:https://www.acwing.com/problem/content/114/

解决思路:我们所要找到的是雷达的最小数目,因此我们需要对每个小岛进行大都分析

①求出能够到达他的海岸线的范围,通过d与y我们可以求出他的x轴上的覆盖范围,(l,r),然后按照区间的右端点排序。

②求出每个岛的覆盖范围后,我们需要找出最少的使用雷达的数量,如果当前区间包括下一点的区间范围,则直接跳过;如果当前区间不能包含下一个区间,则增加一个雷达数目,并且将雷达范围更新为下一个雷达的区间。

例如下图所示:只需要三个雷达即可

 

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=10101;
struct node
{
    double l,r;
    bool operator <(const node &t)const
    {
        return r<t.r;
    }
}s[N];
int main()
{
    int i,j,n,d,x,y;
    bool flag=false;
    scanf("%d%d",&n,&d);
    for(i=0;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        if(y>d)
            flag=true;
        else
        {
            double len=sqrt(d*d-y*y);
            s[i].l=x-len;s[i].r=x+len;//计算每个岛的区间
        }
    }
    if(flag==true)
    {
        puts("-1");
    }
    else
    {
        sort(s,s+n);//按照区间右端点排序
        int cnt=0;
        double nowl=-1e20;
        for(i=0;i<n;i++)
        {
            if(nowl<s[i].l)//判断当前取件是否有下一个区间的点
            {
                cnt++;
                nowl=s[i].r;//更新区间
            }
        }
        printf("%d\n",cnt);
    }
    return 0;
}

 

2.付账问题

题目链接:https://www.acwing.com/problem/content/1237/

解题思路:计算最小的方差,S是固定的,因此AVG也是固定的,只要每个人所掏的钱数越接近于AVG,那么方差就会越小

①首先如果每个人的钱数都超过AVG,那么方差就是0

②如果有的人可能钱包小于AVG,那么不够的钱应该分摊到那些钱包有剩余的人那里,因此,我们首先需要将每个人的钱进行从小到大排序,分别计算当前人数的平均值,看当前这个人的钱包是否足够,不够就掏出自己目前所有的钱,然后,到第二个人的时候需要掏的钱:(S-a[0])/(n-1),如果也不够,也是掏出自己目前所有的钱,到第三个人的时候需要掏出:(S-a[0]-a[1])/(n-2)依次类推,算出最小方差,进而算出最小标准差,再保留4为小数。

代码:

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

int main()
{
    int i,j;
    cin>>n>>s;
    for(i=0;i<n;i++)
        cin>>a[i];
    sort(a,a+n);
    double ans=0,avg=s/n;
    for(i=0;i<n;i++)
    {
        double cur=s/(n-i);
        if(a[i]<cur)
            cur=a[i];
        ans+=(cur-avg)*(cur-avg);
        s-=cur;
    }
    printf("%.4lf",sqrt(ans/n));
    return 0;
}

 

3.乘积最大

题目链接:https://www.acwing.com/problem/content/description/1241/

解题思路:双指针算法,根据K的奇偶分别计算

当K是偶数,结果非负,因为,如果负数是偶数个,负负得正,结果为非负;如果负数是奇数个,那就选偶数个绝对值最大的负数

k 如果是奇数个的话,
(1)所有的数字如果都是负数,那么选出来的结果也一定都是负数
(2)否则的话,则一定至少有 1个非负数, 那么我们将最大的数取出来, 此时要选的个数就是 k--,
# k-- 是偶数,那么就又转化为 k-- 是偶数的情况思考

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
int n,k;
const int mod=1000000009;
const int N=100010;
int a[N];
int main()
{
    int i,j;
    cin>>n>>k;
    for(i=0;i<n;i++)
        cin>>a[i];
    sort(a,a+n);
    ll res=1,l=0,r=n-1;
    int sign=1;
    if(k%2)
    {
        res=a[r--];
        k--;
        if(res<0)
            sign=-1;
    }
    while(k)
    {
        ll x=(ll)a[l]*a[l+1];
        ll y=(ll)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;
    }
    printf("%lld",res);
    return 0;
}

 

4.灵能传输

题目:https://www.acwing.com/problem/content/1250/

解题思路:前缀和,找出最佳单调有序序列

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=300010;
int T;
ll a[N];//存放最初灵能值
ll s[N];//存放前缀和
ll f[N];//存放最终的有序序列

bool st[N];

int main()
{
    cin>>T;
    while(T--)
    {
        int n,i,j;
        cin>>n;
        s[0]=0;
        for(i=1;i<=n;i++)
        {
            cin>>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(i=0;i<=n;i++)
        {
            if(s[i]==s0)
            {
                s0=i;
                break;
            }
        }

        for(i=0;i<=n;i++)
        {
            if(s[i]==sn)
            {
                sn=i;
                break;
            }
        }

        memset(st,0,sizeof(st));
        int l=0,r=n;
        for(i=s0;i>=0;i-=2)
        {
            f[l++]=s[i];
            st[i]=true;
        }
        for(i=sn;i<=n;i+=2)
        {
            f[r--]=s[i];
            st[i]=true;
        }
        for(i=0;i<=n;i++)
        {
            if(st[i]==false)
                f[l++]=s[i];
        }
        ll res=0;
        for(i=1;i<=n;i++)
        {
            res=max(res,abs(f[i]-f[i-1]));
        }
        cout<<res<<endl;
    }
    return 0;
}

 

5.后最表达式

题目:https://www.acwing.com/problem/content/1249/

解题思路:这道题特别坑,以为很简单,但是内藏玄机。

①我们需要判断负号的个数,如果一个都没有,那就全部相加

②如果负号个数不为0,那么我们要求最大数,故此假设有3个负号1个正号,我们按从小到大排序,最大的数的计算式为:

a5-(a1-a2-a3)+a4=a5+a4+a3+a2-a1

因此,当符号个数大于0时,我们选择减去的数介于1~m之间,由于我们要求最大的数,因此尽可能的减去极少的数,故而只需要减去最小的即可

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=100010;
ll a[2*N];

int main()
{
    int n,m,i,j;
    cin>>n>>m;
    for(i=0;i<n+m+1;i++)
        cin>>a[i];
    sort(a,a+n+m+1);
    ll res=0;
    if(m==0)
    {
        for(i=0;i<n+m+1;i++)
            res+=a[i];
        cout<<res<<endl;
    }
    else
    {
        res=a[n+m]-a[0];
        for(i=1;i<n+m;i++)
            res+=abs(a[i]);
        cout<<res<<endl;
    }
    return 0;
}

 

posted @ 2020-06-19 22:08  清风紫雪  阅读(219)  评论(0编辑  收藏  举报