庙会捷运Fair Shuttle

虽然,农民伯伯JOHN可以轻松地在庙会上游览,拿拿奖品或看看节目,可是他的奶牛们非常缺乏锻炼,游览完一整

天的庙会,他们已经筋疲力尽。为了让奶牛们也能开心地游览庙会,农民伯伯JOHN要自己选择捷运卡车线路,让他
们的奶牛可以不费吹灰之力在庙会的不同展台穿梭自由。 可是,农民伯伯JOHN木有(没有)米(钱),他可付不
起豪华线路的钱,所以他选的捷运只能停N(1<= N<= 20,000) 个展台,而且某个线路不能有丝毫重复。奶牛们自
主分为了 K(1<=K<=50,000)组,被编号为1..K。第i组的奶牛中有M_i(1<=M_i<=N)头想从展台 S_i(1<=S_i<=N). 捷
运由于容量有限,也许不能载下所有想乘坐的奶牛们。组里一部分的奶牛就得单独乘坐咯。 现在,农民伯伯JOHN
经过调查得知,每个捷运卡车的容量是C(1<=C<=100),和每个组的奶牛在庙会里想去参观的不同展台,请你帮农民
伯伯决定最多能有多少头牛可以在庙会上乘坐舒适的捷运卡车。
输入
第1行: 三个整数: K,N,C。 由空格隔开。
第2..K+1行:第i+1行,告诉你第i组奶牛的信息: S_i, E_i and M_i。由空格隔开。
输出
一行:可以在庙会乘坐捷运的牛的最大头数
样例输入 Copy
8 15 3
1 5 2
13 14 1
5 8 3
8 14 2
14 15 1
9 12 1
12 15 2
4 6 1

10
捷运可以把
2头奶牛从展台1送到展台5,
3头奶牛从展台5到展台8,
2头奶牛从展台8到展台14,
1头奶牛从展台9送到展台12,
1头奶牛从展台13送到展台14,
1头奶牛从14送到15。

Sol

我们来考虑一种贪心的思想,按右端点从小到大排一遍序,为什么呢?后面说,然后对排好序的每堆奶牛依次进行遍历,如果当前有空位,空位大于这堆奶牛的数量,那就全上去,不然的话,就能上去几个就上去几个。这样下来的话,结果一定是最优的,其正确性不难证明,因为刚开始我们对每堆奶牛要到的地方从小到大排了序(即终点),那么每个位置最多只有一次奶牛上车,而且这些奶牛来自同一群,所以我们对每堆奶牛分别进行考虑即可,这就是为什么要按右端点排序的原因。贪心过程中,要维护最大值。因为要算最少的空位子,下面给出两种代码:

#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 50007
using namespace std;
int ans,n,m,k,w[maxn];
inline int qread() 
{
  char c=getchar();int num=0,f=1;
  for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
  for(;isdigit(c);c=getchar()) num=num*10+c-'0';
  return num*f;
}
struct node 
{
  int u,v,c;
}e[maxn];
const int inf=0x7fffffff;
inline bool cmp(node a, node b) 
{
  if(a.v!=b.v) 
     return a.v<b.v;
  return a.u<b.u;
}
int main() 
{
  k=qread(),n=qread(),m=qread();
  for(int i=1;i<=k;++i)
     e[i].u=qread(),e[i].v=qread(),e[i].c=qread();
  sort(e+1,e+1+k,cmp);
  for(int i=1;i<=k;++i) 
  {
    if(w[e[i].u]>=m) 
	//在这个时刻车上已有多于M个人了 
	   continue;
    int minn=inf;
    for(int j=e[i].u;j<=e[i].v;++j) 
    //统计这个时间段,可以上多少人 
	{
      minn=min(m-w[j],minn);
      if(minn<=0) 
	     break;
    }
    if(minn>0) //如果可以上人的话 
	{
      if(minn>=e[i].c) //如果可以全部上 
	  { 
        for(int j=e[i].u;j<e[i].v;++j)
          w[j]+=e[i].c;
        ans+=e[i].c;
      }
      else //只能上一部分 
	  {
        for(int j=e[i].u;j<e[i].v;++j)
            w[j]+=minn;
        ans+=minn;
      }
    }
  }
  printf("%d\n",ans);
  return 0;
}

  线段树:

#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 20007
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int inf=0x7fffffff;
int n,k,m,maxx[maxn<<2],lazy[maxn<<2],zrj,w[50007];
inline int qread() {
  char c=getchar();int num=0,f=1;
  for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
  for(;isdigit(c);c=getchar()) num=num*10+c-'0';
  return num*f;
}
struct node {
  int u,v,c;
}e[50007];
inline bool cmp(node a, node b) {
  if(a.v!=b.v) return a.v<b.v;
  return a.u<b.u;
}
inline void pushup(int rt) {
  maxx[rt]=max(maxx[ls],maxx[rs]);
}
inline void pushdown(int rt) {
  if(lazy[rt]) {
    maxx[ls]+=lazy[rt],lazy[ls]+=lazy[rt];
    maxx[rs]+=lazy[rt],lazy[rs]+=lazy[rt];
    lazy[rt]=0;
  }
}
void modify(int rt, int l, int r, int L, int R, int val) {
  if(L>r||R<l) return;
  if(L<=l&&r<=R) {
    lazy[rt]+=val,maxx[rt]+=val;
    return;
  }
  int mid=(l+r)>>1;
  pushdown(rt);
  modify(ls,l,mid,L,R,val),modify(rs,mid+1,r,L,R,val);
  pushup(rt);
}
int cmax(int rt, int l, int r, int L, int R) {
  if(L>r||R<l) return -inf;
  if(L<=l&&r<=R) return maxx[rt];
  int mid=(l+r)>>1,ans=-inf;
  pushdown(rt);
  if(L<=mid) ans=max(ans,cmax(ls,l,mid,L,R));
  if(R>mid) ans=max(ans,cmax(rs,mid+1,r,L,R));
  return ans;
}
int main() {
  k=qread(),n=qread(),m=qread();
  for(int i=1;i<=k;++i) e[i].u=qread(),e[i].v=qread(),e[i].c=qread();
  sort(e+1,e+1+k,cmp);
  for(int i=1;i<=k;++i) {
    int p=cmax(1,1,n,e[i].u,e[i].v-1);
    int minn=min(m-p,e[i].c);
    zrj+=minn,w[i]+=minn;
    modify(1,1,n,e[i].u,e[i].v-1,w[i]);
  }
  printf("%d\n",zrj);
  return 0;
}

  

 

 

Sol:

对于此题,易知我们更喜欢那些到站点小一些的牛,于是按结束位置从小到大进行排序。
那么每头牛如何选择座位呢?
如下图如示

 

 

 


按照排序后的顺序,奶牛1会排在前面。如果奶牛1抢了座位1,那么奶牛2就坐不进去了。
而奶牛1的时间实际上是可以坐进座位2的,这样给空位更大的座位1留下了创造更大价值的机会。
由此可以知道,奶牛要去接在结束时间最早且在自己开始之前结束的那个座位上。
因为座位数比较小,所以可以每次都排序,
否则可需要借助线段树来降低复杂度

下图中seat1~seat3为3个座位释放出来的时间,sum代表输运了多少头牛

 

 

#include<bits/stdc++.h>
using namespace std;
struct act
{
    int s,t,num;
    friend bool operator <(act a,act b)
    {
        
            return a.t<b.t;
        
    }
}a[50010];
int End[111];
bool cmp(int x,int y)
{
    return x>y;
}
int main()
{
    int k,n,c;
    scanf("%d%d%d",&k,&n,&c);
    for(int i=1;i<=k;++i)
        scanf("%d%d%d",&a[i].s,&a[i].t,&a[i].num);
    std::sort(a+1,a+1+k);
 
    int sum=0;
    for(int i=1;i<=k;++i)//考虑每个批次的牛
    {
         std::sort(End+1,End+1+c,cmp);//降序排列
  
        for(int j=1;a[i].num&&j<=c;++j)
        //对于每个座位,如果它的释放时间小于当前牛上车的时候
            if(End[j]<=a[i].s) //优先使用早释放出来的座位
            {
                ++sum;
                End[j]=a[i].t; //更新这个座位的释放时间
                a[i].num--;
            }
    
    }
    printf("%d\n",sum);
    return 0;
}

  线段树维护

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())   x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
inline void print(int x){
	if (x>=10) print(x/10);
	putchar(x%10+'0');
}
const int N=5e4,M=2e4;
int n,m,K;
int root=1;
struct AC{
	int l,r,val;
	void join(int x,int y,int z){l=x,r=y,val=z;}
	bool operator <(const AC &x)const
	     {
		     return r!=x.r?r<x.r:l>x.l;
		 }
}A[N+10];
struct Tree
{
	#define ls (p<<1)
	#define rs ((p<<1)|1)
	int Min[M*16+10],lazy[M*16+10];
	void updata(int p){Min[p]=min(Min[ls],Min[rs]);}
	void pushdown(int p)
	{//“懒惰”标记
		if (!lazy[p])	
		    return;
		lazy[ls]+=lazy[p];
		lazy[rs]+=lazy[p];
		Min[ls]+=lazy[p];
		Min[rs]+=lazy[p];
		lazy[p]=0;
	}
	void build(int p,int l,int r)
	{
		if (l==r)
		 {
		      Min[p]=K;
			  return;
		 }
			//开始空位为K
		int mid=(l+r)>>1;
		build(ls,l,mid),build(rs,mid+1,r);
		updata(p);
	}
	int get(int p,int l,int r,int x,int y)
	{
		pushdown(p);
		if (x<=l&&r<=y)	
		    return Min[p];
		int mid=(l+r)>>1,ans1=inf,ans2=inf;
		if (x<=mid)	
		    ans1=get(ls,l,mid,x,y);
		if (y>mid)	
		    ans2=get(rs,mid+1,r,x,y);
		if (ans1==inf&&ans2==inf)	
		    return 0;
		return min(ans1,ans2);
	}
	void change(int p,int l,int r,int x,int y,int t)
	{
		pushdown(p);
		if (x<=l&&r<=y)
		   {
		       Min[p]+=t,
			   lazy[p]+=t;
			   return;
		   }
		int mid=(l+r)>>1;
		if (x<=mid)	
		    change(ls,l,mid,x,y,t);
		if (y>mid)	
		    change(rs,mid+1,r,x,y,t);
		updata(p);
	}
}T;
int main(){
	n=read(),m=read(),K=read();
	int ans=0;
	for (int i=1,x,y,z;i<=n;i++)	
	x=read(),y=read()-1,z=read(),A[i].join(x,y,z);
	//因为在时刻r奶牛已经下车了,所以右端点要--
	sort(A+1,A+1+n);
	T.build(1,1,m);
	for (int i=1;i<=n;i++)
	{
		int l=A[i].l,r=A[i].r;
		int tmp=min(A[i].val,T.get(1,1,m,l,r));
		//看看能上多少奶牛,上不了的就干脆别上了
		if (tmp)	
		T.change(1,1,m,l,r,-tmp),ans+=tmp;
		//更新,包括答案的更新和线段树的更新
	}
	printf("%d\n",ans);
	return 0;
}

  

这道题我们维护两个堆 按终点距离为键值 维护一个大根堆 一个小根堆

我们每次到一个点 就把所有到这个点(作为终点)的奶牛扔出堆(大根堆小根堆的信息要互通)

表示他们已经下车了 到一个点之后把所有在这个点上车的牛全部扔进堆 表示他们要上车辣QAQ

如果这个时候车上的人太多了(会出事的2333) 我们就贪心地把终点最远的扔出堆

这样就可以得到最优解了2333

https://www.cnblogs.com/lyzuikeai/p/7575850.html

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
const int M=3e4+7;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9')
	{ans=ans*10+(c-'0'); 
	c=getchar();}
    return ans*f;
}
int f[2*M],cnt,cost,k,n,c,ans;
struct pos
{
    int id,h,ed;
    bool operator <(const pos &x)
	const{return x.ed<ed;}
};
std::vector<pos>e[M];
std::priority_queue<pos>q1;
struct Q
{
    int id,h,ed;
    bool operator <(const Q &x)
	const{return x.ed>ed;}
};
std::priority_queue<Q>q2;
int main()
{
    int v,x,y;
    k=read(); //k个运输 
	n=read(); //n个站点 
	c=read(); //c个位子 
    for(int i=1;i<=k;i++) 
	x=read(),y=read(),v=read(),
	e[x].push_back((pos){++cnt,v,y});
    for(int i=1;i<=n;i++)
	{
        while(!q1.empty())
		{
            pos x=q1.top(); 
            if(x.ed>i) 
			   break;
            q1.pop();
            if(f[x.id])
			    continue;
            f[x.id]=1; 
			cost-=x.h; 
			ans+=x.h; 
        }
        int mx=e[i].size();
        for(int j=0;j<mx;j++)
		{
            q1.push((pos){e[i][j].id,e[i][j].h,e[i][j].ed});
            q2.push((Q){e[i][j].id,e[i][j].h,e[i][j].ed});
            cost+=e[i][j].h;
        }
        while(cost>c)
		{
            Q x=q2.top(); 
			q2.pop();
            if(f[x.id]) 
			   continue;
            f[x.id]=1;
            if(cost-c>=x.h)
			   {
			    cost-=x.h; 
			    continue;
			    }
            int now=cost-c;
            cnt++;
            q1.push((pos){cnt,x.h-now,x.ed});
            q2.push((Q){cnt,x.h-now,x.ed});
            cost=c;
        }
    }
    printf("%d\n",ans);
    return 0;
}

  

posted @ 2020-11-23 21:46  我微笑不代表我快乐  阅读(3)  评论(0编辑  收藏  举报