两道中等贪心题,poj 3040, poj 3190

POJ 3190  Stall Reservations

思路:利用优先队列,先对区间左端点从小到大排序,优先队列以区间右端点小的为优先级。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
struct dd{
    int l;
    int r;
    int ps;
    friend bool operator<(struct dd k1,struct dd k2){
        return k1.r>k2.r;//以右端点小的为优先级,使得队首的牛是最早使用完机器的牛。
    }
}ss[50005];
typedef struct dd dd;
bool cmp(dd a,dd b){
    return a.l<b.l;
}
int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        int s[50005],need=1;//need为需要的机器数,s[i]表示输入时顺序为i的牛所使用的机器编号。
        for(int i=0;i<n;i++){
            scanf("%d%d",&ss[i].l,&ss[i].r);
            ss[i].ps=i;//记录牛的输入时顺序,因为后面sort会打乱顺序。
        }
        sort(ss,ss+n,cmp);
        priority_queue<dd> q;
        q.push(ss[0]);
        s[ss[0].ps]=1;
        for(int i=1;i<n;i++){
            if(ss[i].l<=q.top().r)//如果队首的牛还在使用机器,则需再拿一台机器。
                s[ss[i].ps]=++need;
            else{//如果队首的牛已经使用完机器,则使用他的机器。
                s[ss[i].ps]=s[q.top().ps];
                q.pop();
            }
            q.push(ss[i]);
        }
        printf("%d\n",need);
        for(int i=0;i<n;i++)
            printf("%d\n",s[i]);
    }
    return 0;
}


POJ 3040 Allowance

思路:面额大于c的直接给。对于小于c的,先取面额大的,取到不能再取(即再取就会溢出),然后取面额小的取,使得工资尽可能的接近c。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define INF 10000000
using namespace std;

struct dd{
    int v;
    int b;
}uu[22];
int tt[22];//tt[i]表示在其中一种组合方式中,第i个面值的所需要的个数(不完全是,见34行)。
bool cmp(struct dd a,struct dd b){
    return a.v>b.v;
}
int main()
{
    int n,c;
    while(scanf("%d%d",&n,&c)!=EOF){
        int k=0,sum=0;
        for(int i=0;i<n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x>=c) sum+=y;//面值超出c的直接就可以给。
            else{
                uu[k].v=x;
                uu[k++].b=y;
            }
        }
        sort(uu,uu+k,cmp);
        while(1){//枚举所有工资的组合方式。
            int t=c;
            memset(tt,0,sizeof(tt));
            for(int i=0;i<k;i++){
                tt[i]+=min(t/uu[i].v,uu[i].b);//如果第i个面值的纸币实际数量不够,则直接存储实际数量。
                t-=tt[i]*uu[i].v;
                if(t<0){
                    t+=tt[i]*uu[i].v;//不能溢出。
                    tt[i]=0;//不取
                    break;
                }
                else if(t==0) break;
            }
            if(t>0){
                for(int i=k-1;i>=0;i--){
                    if(uu[i].b){
                        if(uu[i].b<=tt[i]) continue;// !!! 
                        int s=t%uu[i].v?t/uu[i].v+1:t/uu[i].v;//除法可加快速度。
                        s=min(s,uu[i].b);
                        tt[i]+=s;
                        t-=s*uu[i].v;
                        if(t<=0) break;
                    }
                }
            }
            if(t>0) break;
            int d=INF;//可发的周数。
            for(int i=0;i<k;i++){
                if(tt[i])
                    d=min(d,uu[i].b/tt[i]);
            }
            sum+=d;
            for(int i=0;i<k;i++){
                uu[i].b-=d*tt[i];//更新每种纸币的数量。
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}
/*
3 6
4 3
2 2
1 1
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值