贪心算法

本文精选了多个经典的贪心算法题目,通过实际代码详细解析了资源占用问题、任务调度、考试安排等典型应用场景,帮助读者理解贪心策略在算法设计中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ZOJ 1029

资源占用问题

求出最大的重叠次数*10是答案

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,s[201],a,b,i;
        memset(s,0,sizeof(s));
        cin>>n;
        while(n--)
        {
            cin>>a>>b;
            if(a>b)a^=b^=a^=b;
            a++;b++;//注意走廊位置与房间号的关系即可
            for(i=a/2;i<=b/2;i++)s[i]++;
        }
        int ma=0;
        for(i=0;i<201;i++)if(ma<s[i])ma=s[i];
        printf("%d\n",ma*10);
    }
    return 0;
}

 

UVA 1344 Tian Ji -- The Horse Racing

题意:田忌和齐王各有n匹马。已知所有马的速度。。赢一场赚200,输一场输200,平局没输赢。要求出田忌最好的情况。。。其实就是田忌赛马啦。。

思路:贪心。。先把2人的马存成数组。从小到大排序。。

然后情况比较多。。这样考虑:

如果田忌最好的马比齐王最好的马快,就比赛。赚200.。

如果田忌最好的马比齐王最好的马慢,就拿田忌最垃圾的马和齐王这匹马比赛。亏200.。

如果田忌最好的马比齐王最好的马速度相同,就比较田忌和齐王最垃圾的马

如果田忌最垃圾的马比齐王最垃圾的马快,就比赛。赚200.。

如果田忌最垃圾的马比齐王最垃圾的马慢,就拿田忌的这匹马和齐王最好的马比赛。亏200.。

如果田忌最垃圾的马比齐王最垃圾的马速度相同,就比较田忌这匹马和齐王最好的马。

田忌最垃圾的马速度只可能小于等于齐王最好的马

如果田忌最垃圾的马和齐王最好的马相同。那么说明他们剩下马的速度都相同。。后面全是平局,直接结束判断

如果田忌最垃圾的马比齐王最好的马慢,就比赛,亏200.

#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
    int n,a[1001],b[1001];
    while(~scanf("%d",&n))
    {
        int i,j,k=0,m=0;
        int ans=0;
        for(i=0;i<n;i++)scanf("%d",&a[i]);
        for(i=0;i<n;i++)scanf("%d",&b[i]);
        sort(a,a+n);
        sort(b,b+n);
        for(i=n-1,j=n-1;i>=k;)
        {
            if(a[i]>b[j]){
                i--;
                j--;
                ans+=200;
            }
            else if(a[i]<b[j])
            {
                j--;
                k++;
                ans-=200;
            }
            else{
                if(a[k]>b[m])
                {
                    k++;
                    m++;
                    ans+=200;
                }
                else if(a[k]<b[m])
                {
                    k++;
                    j--;
                    ans-=200;
                }
                else{
                    if(a[k]==b[j])break;
                    else
                    {
                        k++;
                        j--;
                        ans-=200;
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

ZOJ1076 很简单的贪心
这题就是任务调度,给定任务的起始和结束时间,求能完成任务的最大数目。
贪心,先按任务的结束时间递减排序,再逐个选择。选择活动的起始时间大于已选择活动的结束时间

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
struct ss
{
    int x1,x2;
    int ord;
}a[1010];
bool cmp(ss a,ss b)
{
    if(a.x2!=b.x2)return a.x2<b.x2;
    return a.x1<b.x1;
}
int main()
{
    int n,i;
    vector<int>s;
    while(~scanf("%d",&n)&&n)
    {
        s.clear();
        for(i=0;i<n;i++){
                scanf("%d%d",&a[i].x1,&a[i].x2);
                a[i].ord=i+1;
        }
        sort(a,a+n,cmp);
        s.push_back(a[0].ord);
     //   for(i=0;i<n;i++)printf("%d %d %d\n",a[i].x1,a[i].x2,a[i].ord);
        int en=a[0].x2;
        for(i=1;i<n;i++)
        {
            if(a[i].x1>=en){
                    s.push_back(a[i].ord);
                    en=a[i].x2;
            }
        }
        for(i=0;i<s.size();i++)
        {
            if(i)printf(" %d",s[i]);
            else printf("%d",s[i]);
        }
        printf("\n");
    }
    return 0;
}

ZOJ3721 贪心 题意是尽可能每天多安排考试
先按 开始时间排序,早的在前面,开始时间相同时按结束时间排序
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<iostream>
#include<algorithm>
struct ss
{
    int s,t;
    int day,pos;
}a[100010];
bool cmp(ss a,ss b)
{
    if(a.s==b.s)return a.t<b.t;
    return a.s<b.s;
}
bool cmp1(ss a,ss b)
{
    if(a.day==b.day)return a.pos<b.pos;
    return a.day<b.day;
}
using namespace std;
int main()
{
    int n,i;
    while(~scanf("%d",&n))
    {
        for(i=0;i<n;i++){
            scanf("%d%d",&a[i].s,&a[i].t);
            a[i].pos=i+1;
        }
        sort(a,a+n,cmp);
        a[0].day=1;
        for(i=1;i<n;i++){
            if(a[i].s<a[i-1].t){
                a[i].day=a[i-1].day;
              //  a[i].s = max(a[i].s, a[i-1].s);
				a[i].t = min(a[i].t, a[i-1].t);
            }
            else{
                a[i].day=a[i-1].day+1;
            }
        }
        printf("%d\n",a[n-1].day);
        sort(a,a+n,cmp1);
        int day=0;
        for(i=0;i<n;i++){
            if(a[i].day==day){
                printf(" %d",a[i].pos);
            }
            else {
                if(day)printf("\n");
                printf("%d",a[i].pos);
                day++;
            }
        }
        printf("\n");
    }
    return 0;
}


题意:有n个机器,m个任务。每个机器至多能完成一个任务。对于每个机器,有一个最大运行时间xi和等级yi,对于每个任务,也有一个运行时间xj和等级yj。只有当xi>=xj且yi>=yj的时候,机器i才能完成任务j,并获得500*xj+2*yj金钱。问最多能完成几个任务,当出现多种情况时,输出获得金钱最多的情况。

题解:

将任务已x从大到小排序(x相同时已y从大到小排序)。然后也用相同排序方法排序机器。开始遍历任务,找出所有xi(xi>=xj),从中选择yi最小的一个作为这个任务的运行机器。为什么这么贪心,因为若还存在任务(xk,yk)使得这个机器能被使用,但xj>=xk,所以获得金钱更多,优先选择j;若k不能使用这个机器,那么必定也就不存在其他机器能被使用,除非是新加入的机器,但新加入的必定不能完成任务j,所以完成任务保证了最多。

Problem : 4864 ( Task )     Judge Status : Accepted
RunId : 11142745    Language : C++    Author : Kylers
Code Render Status : Rendered By HDOJ C++ Code Render Version 0.01 Beta
#include<cstdio>
#include<cstring>
#include<map>
#include<iostream>
#include<algorithm>
#define N 100010
using namespace std;
struct ss
{
    int xi,yi;
}mat[N],work[N];
bool cmp(ss a,ss b)
{
    if(a.xi==b.xi)return a.yi>b.yi;
    return a.xi>b.xi;
}
map<int,int>q;
int main()
{
    int n,m,i;
    while(~scanf("%d%d",&n,&m))
    {
        
        for(i=0;i<n;i++)scanf("%d%d",&mat[i].xi,&mat[i].yi);
        for(i=0;i<m;i++)scanf("%d%d",&work[i].xi,&work[i].yi);
        sort(mat,mat+n,cmp);
        sort(work,work+m,cmp);
        q.clear();
        int j=0;
        long long num=0,ans=0;
        for(i=0;i<m;i++){
            while(j<n&&mat[j].xi>=work[i].xi){
                q[mat[j].yi]++;
                j++;
            }

            map<int,int>::iterator it=q.lower_bound(work[i].yi);
            if(it!=q.end())
            {
                num++;
                ans+=(500*work[i].xi+2*work[i].yi);
                int t=it->first;
                q[t]--;
                if(q[t]==0)q.erase(t);
            }
        }
        printf("%I64d %I64d\n",num,ans);
    }
    return 0;
}

ZOJ3778

问题转化一下,就是给定n堆石子,每次最多可以选m堆,每堆里只能取走一个石子,问最快几次取完。

最多的一堆棋子的个数为t,石子总个数为sum。

若t*m>=sum,

这时,t-1次内是不能取完所有石子的,故至少需要t次,下面是一种t次取完的做法。

画一个t*m的棋盘(t行m列),把a1从第1列最下角开始,一个一个从下向上地放进第一列,第一个石子放在(t,1),第2个放在

(t-1,1)...然后放a2,a3....当i列放慢,就从(t,i+1)开始从下向上放。

这样,这个棋盘足够容纳所有石子。对任意一堆石子ai,其中的石子要么全在一列,要么在2个相邻的列,列i,列i+1,

同时,因为ai<=t,故ai中的任意2个石子不在同一行。

然后,我们每次取走一行,就能用t步取完。

若t*m<sum,此时令f=sum/m+(sum%m?1:0);

由于(f-1)*m<sum,f*m>=sum

故f-1次内不能取完,而我们可以用f次取完。

首先f>t


#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m,i;
        int ma=-1,x;
        int s=0;
        scanf("%d%d",&n,&m);
        for(i=0;i<n;i++)
        {
            scanf("%d",&x);
            if(x>ma)ma=x;
            s+=x;
        }
        int ans=s/m;
        if(s%m)ans++;
        if(ma>ans)printf("%d\n",ma);
        else printf("%d\n",ans);
    }
    return 0;
}
有n个问题
2014 Multi-University Training Contest 2

hdu4882ZCC Loves Codefir

题意 
有n个问题
第二行为解决每个问题需要的时间t
第三行为解决每个问题的系数k
t总*k及解决每个问题的罚时数,要求总罚时最少
对问题进行两两比较,把罚时小的放前面
另外杭电上有点坑,用%lld输出吃了wa
代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<iostream>
#include<algorithm>
#define N 100010
using namespace std;
struct ss{
    int t,k;
}a[N];
int cmp(ss a,ss b){
   // return a.k*b.t>b.k*a.t;
    return a.t*a.k+(a.t+b.t)*b.k<b.t*b.k+(a.t+b.t)*a.k;
}
int main()
{
    int n,i;
    while(~scanf("%d",&n))
    {
        for(i=0;i<n;i++)scanf("%d",&a[i].t);
        for(i=0;i<n;i++)scanf("%d",&a[i].k);
        sort(a,a+n,cmp);
      //  for(i=0;i<n;i++)printf("%d %d\n",a[i].t,a[i].k);
        long long s=0,tsum=0;
        for(i=0;i<n;i++){
            tsum+=a[i].t;
            s+=tsum*a[i].k;
        }
        cout<<s<<endl;
    }
}

ZOJ3715

题意:

有n个学生要选出班长编号从1到n,1号人比较虚荣,想自己当班长,每个人都可以投一票给自己心目中的人但不能投给自己,只要谁的票数最高谁就可以当班长。给出每个人心目中的投票人,以及贿赂每个人所需要的糖果,(只要你给了那个人一定数目的糖果他就会支持你) 。问1号如果当班长的话,最少需要的糖果数。

思路:

由于这里的n比较小,我们只要枚举1当班长时的得票数x,然后再将其他人的得票数大于x的变为x-1  (减少的给1并且减少的肯定是所需糖果树最少的),然后检查最后1的得票数,如果大于x那么肯定无解,如果等于x,只要保证2到n中有得票数<= x - 2的即可(1要投票给别人)。如果小于x,那么从剩下没有支持1的中,找出所需糖果树最少的来贿赂得票知道等于x为止。


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct ss{
    int f,c;
}a[110],b[110];
int v[110],vv[110];
int cmp(ss a,ss b){
    return a.c<b.c;
}
int main()
{
    int t,n,i,j,k;
    scanf("%d",&t);
    while(t--){
        memset(v,0,sizeof(v));
        scanf("%d",&n);
        for(i=2;i<=n;i++){
                scanf("%d",&a[i].f);
                v[a[i].f]++;
        }
        for(i=2;i<=n;i++)scanf("%d",&a[i].c);
        sort(a+2,a+n+1,cmp);
        int s;
        int res=1000000000;
        for(i=v[1];i<=n;i++)
        {
            for(j=2;j<=n;j++){
                b[j].f=a[j].f;
                b[j].c=a[j].c;
            }
            for(j=1;j<=n;j++)vv[j]=v[j];
            s=0;
            for(j=2;j<=n;j++){
                if(b[j].f!=1&&vv[b[j].f]>0&&vv[b[j].f]>=i){
                 //   printf("*** %d %d\n",i,j);
                    s+=b[j].c;
                    vv[b[j].f]--;
                    vv[1]++;
                    b[j].f=1;
                }
            }
            if(vv[1]>i)continue;

            if(vv[1]<i) {
                for(j=2;j<=n;j++){
                    if(vv[1]==i)break;
                    if(b[j].f!=1&&vv[b[j].f]>0){
                        s+=b[j].c;
                        vv[b[j].f]--;
                        vv[1]++;
                        b[j].f=1;
                    }
                }
            }
            if(vv[1]==i){
                int f=0;
                for(j=2;j<=n;j++){
                    if(vv[j]<i-1){
                        f=1;
                        break;
                    }
                }
                if(f){
                    res=min(res,s);
                }
            }
        }
        printf("%d\n",res);
    }
    return 0;
}

ZOJ 3508
士兵选兵器,使装备兵器的士兵尽可能多
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
struct soldier
{
    int minw,maxw;
};
bool cmp(soldier a,soldier b)
{
    return a.maxw<b.maxw;
}
int main()
{
    int n,m,i,x,ans;
    soldier t;
    while(cin>>n>>m)
    {
        vector<soldier> v;
        for(i=0; i<n; i++)
        {
            cin>>t.minw>>t.maxw;
            v.push_back(t);
        }
        sort(v.begin(),v.end(),cmp);//以maxw排序
        multiset<int> mt;
        multiset<int>::iterator it;
        for(i=0; i<m; i++)
        {
            cin>>x;
            mt.insert(x);//自动升序排列
        }
        ans=0;
        for(i=0; i<n; i++)
        {
            it=mt.lower_bound(v[i].minw);//返回不小于 v[i].minw的第一个值的地址
            if((it!=mt.end())&&(*it<=v[i].maxw))
            {
                ans++;
                mt.erase(it);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

HDU4883 区间选点

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=24*60;
int dp[N];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int m,i,val,h1,h2,m1,m2,p1,p2;
        char t1[10],t2[10];
        memset(dp,0,sizeof(dp));
        scanf("%d",&m);
        for(i=0;i<m;i++)
        {
            scanf("%d%s%s",&val,t1,t2);
            sscanf(t1,"%d:%d",&h1,&m1);
            sscanf(t2,"%d:%d",&h2,&m2);
            p1=h1*60+m1;
            p2=h2*60+m2;
            dp[p1]+=val;
            dp[p2]+=-val;
        }
        int ma=0;
        for(i=0;i<24*60;i++){
            dp[i]=dp[i-1]+dp[i];
            ma=dp[i]>ma?dp[i]:ma;
        }
        printf("%d\n",ma);
    }
 
    return 0;
}

HDU4844

题意,就是有N种炒饭,每次炒的时间是t分钟,每次最多炒k份,然后按照进店的顺序给出m个顾客的信息,进店时间,炒饭的编号以及份数。然后要输出每个顾客离开的时间。

题目中告诉了我们炒饭的规则,按照先来先服务,但是每次炒会炒尽可能多的份数,不过不会有多余的。

举个例子,比如每次可以炒5份,每次5分钟。

第一个顾客08:00进来,点了2份A,

第二个顾客08:04进来,点了3份A。

在08:00开始炒的话,由于这个时候第二个顾客还没进来,所以就只炒2份,第一个顾客在08:05离开,这时才炒第二个的3份,所以第二个离开时间是08:10。

同样是每次可以炒5份,每次5分钟。

第一个顾客08:00进来,点了6份A,

第二个顾客08:01进来,点了5份B,

第三个顾客08:02进来,点了4份A。

同样地,先炒5份给第一个,还差一份,这是已经是08:05了,第三个顾客也进来了,所以这时直接炒5份A(因为会尽可能多地炒),08:10第一个和第三个可以同时离开。接着才炒第二个的。


#include<iostream>
#include<cstdio>
using namespace std;
struct ss
{
    int timer, type, num, ans;
}a[1010];
int gettime(char *s)
{
    return ((s[0]-'0')*10+s[1]-'0')*60+(s[3]-'0')*10+s[4]-'0';
}
int main()
{
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        int n,t,k,m,i,j;
        char ti[10];
        scanf("%d%d%d%d",&n,&t,&k,&m);
        for(i=0;i<m;i++)
        {
            scanf("%s",ti);
            a[i].timer=gettime(ti);
            scanf("%d%d",&a[i].type,&a[i].num);
        }
        int now,nnum,tmp;
        now=a[0].timer;
        for(i=0;i<m;i++)
        {
            if(!a[i].num)continue;
            tmp=now>a[i].timer?now:a[i].timer;
            now=tmp+(a[i].num+k-1)/k*t;
            nnum=(k-a[i].num%k)%k;
            a[i].ans=now;
            for(j=i+1;j<m;j++)
            {
                if(!a[j].num)continue;
                if(a[j].timer>now-t||nnum==0)break;
                if(a[j].type==a[i].type)
                {
                    if(a[j].num>nnum)
                    {
                        a[j].num-=nnum;
                        nnum=0;
                    }
                    else
                    {
                        nnum-=a[j].num;
                        a[j].num=0;
                        a[j].ans=now;
                    }
                }
            }
        }
        for(i=0;i<m;i++)printf("%02d:%02d\n",(a[i].ans/60)%24,a[i].ans%60);
        if(cas)printf("\n");
    }
    return 0;
}
/*
test:
2
1 9 5 2
00:00 2 7
00:16 2 7

8 1 2 2
00:00 8 3
00:01 8 1

ans:
00:18
00:36

00:02
00:02
*/


zoj3829 贪心
http://www.cnblogs.com/whatbeg/p/4024974.html

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
    int T;
    char str[1010];
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",str);
        int start=0,num=0,step=0;
        for(int i=0;str[i];i++)
        {
            if(str[i]=='*')start++;
            else num++;
        }
        if(start+1>num){
                num=start+1-num;
                step+=num;
        }
        else num=0;
        start=0;
        for(int i=0;str[i];i++)
        {
            if(str[i]>='0'&&str[i]<='9')num++;
            else start++;
            if(start+1>num){
                start--;
                num++;
                step++;
            }
        }
        printf("%d\n",step);
    }
    return 0;
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值