贪心专题

贪心,由局部最优,最终推导出,全部最优。

例题:

loj 10003

每个工作i有两部分组成,第一部分在a中完成,ai,第二部分在b中完成,bi。所有工作需要先完成a再完成b因此,我们要让a时间小的先完成a,然后去完成b,让b时间大的先完成b(也就是说它也要先完成a),这样工作等待时间最少。

那我们怎么选择,a,b的大的跟小的问题呢?

我们开辟一个空间m放,ai与bi的最小值,如果mi与ai相等那么就,将它放在前面完成,如果mi与bi相等,就将它放在后面完成。

具体处理见代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int M=1e5;
int a[M],b[M];
struct node
{
    int v,s;//v放a,b中最小值,因为要将m升序排列,s放它原本的顺序
}m[M];
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
bool cmp(node a,node b)
{
    return a.v<b.v;
}

int main()
{
    int n;
    n=read();
    for(int i=1;i<=n;i++)
    a[i]=read();
    for(int i=1;i<=n;i++)
    b[i]=read();
    for(int i=1;i<=n;i++)
    m[i].v=min(a[i],b[i]),m[i].s=i;
    sort(m+1,m+1+n,cmp);
    int k=0,tot=n+1;
    int ans[M];
    for(int i=1;i<=n;i++)
    {
        if(m[i].v==a[m[i].s])
        {
            k++;
            ans[k]=m[i].s;
        }
        else
        {
            tot--;
            ans[tot]=m[i].s;
        }
    }
    k=0,tot=0;
    for(int i=1;i<=n;i++)
    {
        k+=a[ans[i]];
        if(tot<k) tot=k;
        tot+=b[ans[i]];
    }
    printf("%d\n",tot);
    for(int i=1;i<=n;i++)
    printf("%d ",ans[i]);
    printf("\n");
    return 0;
}

loj10004 智力大冲浪

题意:选手每个回合答一题,每个题,有限制答题时间,跟权值,如果没时间答这道题,这题的分将被扣去。

题解:

对于每个题i,他的最迟答题时间是ti,我们将所有的题目按照题目权重降序排列,对于要答题,他的答题时间是1-ti,那么它最好的答题时间应该选择在,1-ti之中从左数第一个没有被用过的回合,这样才能保证,其他题有时间答。

代码(代码写的很挫,,下一题跟这题同一类型,下一题的代码更值得借鉴):

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int M=1e5;
int vis[M];
struct node
{
    int v,t;
}a[M];
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
bool cmp(node a,node b)
{
    if(a.v==b.v)
    return a.t<b.t;
    return a.v>b.v;
}

int main()
{
    int n,m;
    m=read();
    n=read();
    for(int i=1;i<=n;i++)
        a[i].t=read();
     for(int i=1;i<=n;i++)
        a[i].v=read();
    sort(a+1,a+1+n,cmp);
    int p=1,tim=n;
    while(tim>0)
    {
        p=1;
        while((tim>a[p].t||vis[p])&&p<=n)
        {
            p++;
        }
        if(p<=n)
        {
            vis[p]=1;
            tim--;
        }
        else tim--;
    }
    for(int i=1;i<=n;i++)
    if(vis[i]==0)
    m-=a[i].v;
    printf("%d\n",m);
    return 0;
}

loj10008. 「一本通 1.1 练习 4」家庭作业

由于此题跟上一题题意相同不再解释:

代码(关键在于找它前面第一个没用过的回合,用数组pre标记,每次使用回合后,对pre进行判断更新pre数组):

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
//对于作业按照学分倒序排列后,对于第x天之前需要完成的作业要从x往前找第一个没用过的空间
const int M=1e6+100;
int vis[M];
int pre[M];
struct node
{
    int v,t;
}a[M];
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
bool cmp(node a,node b)
{
    if(a.v==b.v)
    return a.t<b.t;
    return a.v>b.v;
}
int find(int x)
{
    if(!vis[x])
    {
        return x;
    }
    pre[x]=find(pre[x]);
    return pre[x];
}
int main()
{
    int n,ans=0;
    n=read();
    for(int i=1;i<=n;i++)
        a[i].t=read(),a[i].v=read(),pre[i]=i-1;
    sort(a+1,a+1+n,cmp);
   for(int i=1;i<=n;i++)
   {
       int tmp=find(a[i].t);
       if(tmp)
       {
           vis[tmp]=1;
           ans+=a[i].v;
           if(a[i].t!=tmp)
           {
               pre[a[i].t]=pre[tmp];//关键,如果第i天之前的已经不是这一天那么,我们更新到最新的那一天
            }
       }
   }
    printf("%d\n",ans);
    return 0;
}

10005. 「一本通 1.1 练习 1」数列极差

题意:对于一个数列,你拿出其中两个值,a,b得到a*b+1再放入数列中,直到数列剩余一个值。按照不同顺序取值,其中剩余值最大的是max,最小的是min,极差是max-min;

题解:

对于一组数我们想,每次我们执行乘操作,那么我们一直拿其中最小的两个,最终结果一定是最大的(因为,最小的经过这样的乘操作后,会很快变大,然后再与大的相乘,就比原本数列中大的相乘得到的值大)

每次拿最大的两个将得到最小的

代码:

#include <iostream>
#include<queue>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define ll long long
//最大的是从最小的开始加,最小的是从最大的开始加
ll read()
{
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

struct cmp1
{
    bool operator()(ll &a,ll &b)
    {
        return a>b;//最小值优先
    }
};
struct cmp2
{
    bool operator()(ll &a,ll &b)
    {
        return a<b;//最大值优先
    }
};
int main()
{
    int n;
    priority_queue<ll,vector<ll>,cmp1>p1;
    priority_queue<ll,vector<ll>,cmp2>p2;
    while(~scanf("%d",&n)&&n)
    {
        ll x;
        for(int i=0;i<n;i++)
        {
            x=read();
            p1.push(x);
            p2.push(x);
        }
        ll maxn,minn;
        ll t1,t2;
        while(p1.size()>1)
        {
            t1=p1.top();
            p1.pop();
            t2=p1.top();
            p1.pop();
            t1=t1*t2+1;
            p1.push(t1);
        }
        maxn=p1.top();
        p1.pop();
        while(p2.size()>1)
        {
            t1=p2.top();
            p2.pop();
            t2=p2.top();
            p2.pop();
            t1=t1*t2+1;
            p2.push(t1);
        }
        minn=p2.top();
        p2.pop();
        printf("%lld\n",maxn-minn);
    }
    return 0;
}

#10009. 「一本通 1.1 练习 5」钓鱼

题解:

对于五分钟,我们完全可以将五分钟作为进制,就将五分钟的问题化为一分钟:

此题,n最大为100,因此暴力枚举女孩可能走几个鱼塘,然后求出,对于这几个鱼塘,女孩能掉到多少鱼,最后将最大值输出。对于女孩能掉到多少鱼,首先我们将所有可能走过的鱼塘的鱼的初值放入优先队列(降序),然后每次拿第一个,直到剩余时间不足为止。

代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int M=1e6+100;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int f[M],d[M],t[M];
int main()
{
    priority_queue<pair<int,int> >heap;
    int n,T;
    n=read(),T=read();
    T=T*12;
    for(int i=1;i<=n;i++)
    f[i]=read();
    for(int i=1;i<=n;i++)
    d[i]=read();
    for(int i=1;i<=n-1;i++)
    t[i]=read();
    int maxn=-INF;
    int time=0;
    for(int k=1;k<=n;k++)
    {
        int rest_time=T-time;
        int ans=0;
        for(int i=1;i<=k;i++)
        heap.push(make_pair(f[i],i));
        while(rest_time>0&&heap.top().first>0)
        {
            pair<int,int>a=heap.top();
            heap.pop();
            ans+=a.first;
            a.first-=d[a.second];
            heap.push(a);
            rest_time--;
        }
        if(ans>=maxn)
        maxn=ans;
        time+=t[k];
    }
        printf("%d\n",maxn);
    return 0;
}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值