运输问题--替换型贪心

多的不说,看题:

一、游客运输

风景迷人的阆中市,拥有n个美丽景点,编号为1..n。由于慕名而来的游客越来越多,市政府特意安排了一辆容量为C的观光公交车,为游客提供便捷的交通服务。观光公交每天早上从1号景点出发,一次前往2,3,…n号景点,每天只开一趟。
  设某天有m位游客,每位乘车1次,第i位游客希望从景点Li到达景点Ri(1<=Li<=Ri<=n)。
  现在给你观光公交的容量C,同事提供给你每位乘客的信息,请你计算这一天中最多能满足都少个人的愿望。

【输入格式】

  第一行包含两个整数n,m,C,分别表示景点数目、游客数目和观光公交的容量。
第2行到第m+1行,每行包含两个整数:Li,Ri,其中第i+1行表示奶牛i想从景点Li到达Ri。

【输出格式】

  一个整数,表示能满足的游客数量。

【输入样例】

10 8 3
8 10
1 9
2 10
6 8
5 8
6 7
3 5
4 6

【输出样例】

6

【数据范围】

n,m,C<=200000

观察到乘客的区间是一条线段,左右端点有一定的先后顺序。
可以考虑按某一端点排序之后贪心计算;
对于一个乘客,如果有空位,就直接坐
如果没有空位,可以替换一个要做更久的人
想象一列火车从左向右开,乘客依次上车;到了没有位置的时候,这时为了更好地满足后面乘客的需求,可以找一个下车时间在该乘客之后的乘客下车,然后让乘客i上车;
可以用优先队列或者是set维护。
这里用的set:

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#include<set>
using namespace std;
int n,m,c,ans=0;
struct ge
{
    int l,r;
    friend bool operator<(ge x,ge y)
    {
        return x.l<y.l;
    }
}a[200005];
multiset<int>s;
multiset<int>::iterator it;
int main()
{
    scanf("%d%d%d",&n,&m,&c);
    for(int i=1;i<=m;i++) scanf("%d%d",&a[i].l,&a[i].r);
    sort(a+1,a+1+m);
    for(int i=1;i<=m;i++)
    {
        if(a[i].l==a[i].r)
        {
            ans++;
            continue;
        }
        it=s.begin();
        while(!s.empty() && *it<=a[i].l)//删除已经下车的人
        {
            s.erase(it);
            it=s.begin();
        }

        if(s.size()<c)//有空位
        {
            ans++;
            s.insert(a[i].r);
        }
        else //找人替换
        {
            it=--s.end();
            if(*it>a[i].r)
            {
                s.erase(it);
                s.insert(a[i].r);
            }
        }
    }
    printf("%d",ans);
    return 0;
}

二、奶牛航班

【问题描述】
  为了表示不能输给人类,农场的奶牛们决定成立一家航空公司。他们计划每天清晨,从密歇根湖湖岸的最北段飞向最南端,晚上从最南端飞往最北端。旅途中,航空公司可以安排飞机停在某些机场,他们需要你帮助来决定每天携带那些旅客。
  沿着湖岸,有N个由北到南编号为1到N的农场。每个农场都有一个机场,这填,有k群牛想要乘坐飞机旅行。每一群牛想要从一个农场飞往另一个农场,航班可以在某些农场停下来带上部分或全体牛。奶牛们登记后会一直停留直至到达目的地。
  提供给你飞机的容量为C,同时提供给你想要旅行的奶牛信息,请你计算出这一天的航班最多能满足几只奶牛的愿望。

【输入格式】

  第1行有3个整数:K,N,C。接下来的K行,每行包含3个整数:S,E,M,表示有M只奶牛想从农场S乘飞机到农场E。

【输出格式】

  可以完成旅行的奶牛人数的最大值。

【输入样例】

4 8 3
1 3 2
2 8 3
4 7 1
8 3 2

【输出样例】

6

【样例解释】

  有3群奶牛旅行,8个农场,飞机上有3个作为。造成,飞机把2只牛从1带到3,1只从2带到8,1只从4带到7.晚上,航班把2只牛从8带到3。

【数据范围】

1<=N<=10000
1<=K<=50000
1<=C<=100

当然一来一回分两次计算就行了
和上题有不同的地方在于相同路段的乘客有多个:
思路大致相同,但是这里计算容量就不能再用s.size()了,而要新开一个数组c[x]表示在x下车的乘客数量,并用now表示当前车的容量;对于每个乘客来说同样有两种情况,有空位或把人踢下车;
要注意的就是情况有点多,所以写得有点复杂。。。orz
用优先队列实现的;

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
const int maxn=50005;
const int maxm=40005;
int K,n,C,c1,c2,c[maxn],now,last,ans,l,r,s;
struct data{
    int l,r,s;
    friend bool operator <(data a,data b){
        return a.r<b.r;
    }
}t,tt,a[maxn],b[maxn];
bool cmp(data a,data b){
    return a.l<b.l;
}
void work1(){
    priority_queue<data>q; 
    sort(a+1,a+1+c1,cmp);
    now=C,last=0;
    for(int i=1;i<=c1;i++){
        while(!q.empty() && q.top().r<=a[i].l) q.pop();
        while(last<=a[i].l) now+=c[last++];

        if(now){//有位置
            if(now>=a[i].s){
                q.push(a[i]);
                now-=a[i].s;
                ans+=a[i].s;
                c[a[i].r]+=a[i].s;
                a[i].s=0;
            }
            else{
                q.push((data){a[i].l,a[i].r,now});
                ans+=now;
                a[i].s-=now;
                c[a[i].r]+=now;
                now=0;
            }
        }
        if(a[i].s){//把终点在i后面的踢下去 
            s=0;
            while(!q.empty()){
                if(s<a[i].s && a[i].r<q.top().r){
                    if(s+q.top().s<=a[i].s){
                        s+=q.top().s;
                        c[q.top().r]-=q.top().s;
                        q.pop();
                    }
                    else{
                        t=q.top();
                        c[q.top().r]-=a[i].s-s;
                        t.s-=a[i].s-s;
                        s=a[i].s;
                        q.pop();q.push(t);
                    }
                }
                else break;
            }
            if(s){
                q.push((data){a[i].l,a[i].r,s});
                c[a[i].r]+=s;
            }
        }
    }
}
void work2(){
    memset(c,0,sizeof(c));
    priority_queue<data>q; 
    sort(b+1,b+1+c2,cmp);
    now=C,last=0;
    for(int i=1;i<=c2;i++){
        while(last<=b[i].l) now+=c[last++];

        if(now){//有位置
            if(now>=b[i].s){
                q.push(b[i]);
                now-=b[i].s;
                ans+=b[i].s;
                c[b[i].r]+=b[i].s;
                b[i].s=0;
            }
            else{
                q.push((data){b[i].l,b[i].r,now});
                ans+=now;
                b[i].s-=now;
                c[b[i].r]+=now;
                now=0;
            }
        }
        if(b[i].s){//把终点在i后面的踢下去 
            s=0;
            while(!q.empty() && s<b[i].s && b[i].r<q.top().r){
                if(s<b[i].s && b[i].r<q.top().r){
                    if(s+q.top().s<=b[i].s){
                        s+=q.top().s;
                        c[q.top().r]-=q.top().s;
                        q.pop();
                    }
                    else{
                        t=q.top();
                        c[q.top().r]-=b[i].s-s;
                        t.s-=b[i].s-s;
                        s=b[i].s;
                        q.pop();q.push(t);
                    }
                }
                else break;
            }
            if(s){
                q.push((data){b[i].l,b[i].r,s});
                c[b[i].r]+=s;
            }
        }
    }
}
char ch;
void _scanf(int &x){
    x=0;
    ch=getchar();
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
}
int main(){
//  freopen("in.txt","r",stdin);
    scanf("%d%d%d",&K,&n,&C);
    ans=0,c1=c2=0;
    for(int i=1;i<=K;i++){
        _scanf(l);_scanf(r);_scanf(s);
        if(l==r){
            ans++;continue;
        }
        if(l<r) a[++c1]=(data){l,r,s};
        else b[++c2]=(data){r,l,s};
    }
    work1();
    work2();
    printf("%d\n",ans);
    return 0;
}

三、火车运输

【问题描述】

  ByteLand火车站(编号0)每天都要发往全国各地N列客运火车,编号1 N。第i列火车的目的地是编号Si的火车站。

  对任意车站X,都与X+1车站有铁轨直接相连,因此火车站可以看成数轴上的整数点,第i列火车可以停靠区间[0, Si]中的各个站点。每列火车装载乘客的最大容量为Ci。有M个人需要乘坐火车。已知每个人的乘车区间为[Li, Ri],即是说,在Li上车,在Ri下车。

  由于火车的容量限制,请你求出最多有多少人的乘车需求可以得到满足。

【输入格式】

  第1行:2个整数N和M。
  接下来N行,每行2个整数Si和Ci,表示第i辆车的目的站和容量。
  接下来M行,每行2个整数Li和Ri,表示第i个乘客的乘车区间。

【输出格式】

  第1行:1个整数,表示最多有多少乘客的乘车需求可以满足

【输入样例】

1 3
10 2
1 5
3 7
4 9

【输出样例】

2

【数据范围】

对于20%的数据,
N= 1, 1 ≤ M ≤ 10^5
1 ≤Si,Ci ≤ 10^9
1 ≤ Li ≤ Ri ≤ 10^9。

对于另外20%的数据,
1 ≤ N,M ≤ 2000
1 ≤Si,Ci ≤ 10^9
1 ≤ Li ≤ Ri ≤ 10^9。

对于100%的数据,
1 ≤ N,M ≤ 10^5
1 ≤Si,Ci ≤ 10^9
1 ≤ Li ≤ Ri ≤ 10^9。

和二不同的是火车也有多个;
意思就是火车在不同的时间的容量是不同的?!!
但是火车有公共的起点1。我们当然不能让火车开着开着容量就减少了,这样怎么贪心计算。。。所以我们把终点看成起点,火车从终点开向起点,乘客也从终点走到起点,所以我们按照右端点从大到小排序,并从右向左枚举乘客;
对于一个(右端点靠右)乘客,我们要尽可能用右端点靠右的公交装(因为右端点稀有)
所以我们把乘客按照右端点从大到小开始枚举 (一开始把第一个公交也无法装的乘客直接删掉)
枚举每个乘客能否装在公交里;首先应将新加进队伍中的公交(bus[j].r>=pas[i].r)的容量加入now中,且由于容量是now的公交可以看成是now个容量为1的公交,所以我们可以分开考虑c个公交的情况和当前车上的人数,从而简化了模型
我们就可以更新出车的现在可用容量并把装有人的车(的目的地)甩进set中。

转换模型之后就是游客运输了; 
乘客能装有两个选择:
    1.有空车
    2.车上的人比当前乘客下得还早(s.begin()<pas[i].l)
 当然要维护当前车的总可用容量(下车)
 cnt[i]记录在点i的下车人数   
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
using namespace std;
const int maxn=1000005;
int n,m,cnt[maxn],last,ans,now;
struct passenger{
    int l,r;
    friend bool operator <(passenger a,passenger b){
        if(a.r==b.r) return a.l>b.l;
        return a.r>b.r;
    }
}pas[maxn];
struct route{
    int s,c;
    friend bool operator <(route a,route b){
        return a.s>b.s;
    }
}bus[maxn];
multiset<int>s;
multiset<int>::iterator it;
void work(){
    sort(bus+1,bus+1+n);
    sort(pas+1,pas+1+m);
    int i=1,j=1;
    last=bus[1].s;
    while(i<=m && pas[i].r>bus[1].s) i++;
    for(;i<=m;i++){
        while(j<=n && bus[j].s>=pas[i].r){
            now+=bus[j].c,j++;
        }
        while(last>pas[i].r && last>0){
            now+=cnt[--last];
        }
        if(now){
            now--,cnt[pas[i].l]++,ans++;
            s.insert(pas[i].l);
        }
        else{
            it=s.begin();
            if(it==s.end()) continue;
            int t=*it;
            if(t<pas[i].l){
                cnt[t]--,cnt[pas[i].l]++;
                s.erase(it);
                s.insert(pas[i].l);
            }
        }
    }
    printf("%d\n",ans);
}
char ch;
void _scanf(int &x){
    x=0;
    ch=getchar();
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
}
int main(){
//  freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        _scanf(bus[i].s);_scanf(bus[i].c);
    }
    for(int i=1;i<=m;i++){
        _scanf(pas[i].l);_scanf(pas[i].r);
    }
    work();
    return 0;
}

反正就是更新状态+替换啦~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值