运输问题--替换型贪心

来源:互联网 发布:mac怎么新建txt文件 编辑:程序博客网 时间:2024/05/18 09:22

多的不说,看题:

一、游客运输

风景迷人的阆中市,拥有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;}

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

原创粉丝点击