廊桥分配(CSP-S-2021-T1)通俗题解

  • Post author:
  • Post category:其他



目录


题目描述


输入输出样例


题解:


方案一(朴素):


方案二(找规律优化):


Code(优化):


Code(不要喷我^_O_^)(35)(后面还有)


Code(55)(我也服了我自己)


Gift For you


题目描述

当一架飞机抵达机场时,可以停靠在航站楼旁的廊桥,也可以停靠在位于机场边缘的远机位。乘客一般更期待停靠在廊桥,因为这样省去了坐摆渡车前往航站楼的周折。然而,因为廊桥的数量有限,所以这样的愿望不总是能实现。

机场分为国内区和国际区,国内航班飞机只能停靠在国内区,国际航班飞机只能停靠在国际区。一部分廊桥属于国内区,其余的廊桥属于国际区。

L 市新建了一座机场,一共有 n 个廊桥。该机场决定,廊桥的使用遵循“先到先得”的原则,即每架飞机抵达后,如果相应的区(国内/国际)还有空闲的廊桥,就停靠在廊桥,否则停靠在远机位(假设远机位的数量充足)。该机场只有一条跑道,因此不存在两架飞机同时抵达的情况。

现给定未来一段时间飞机的抵达、离开时刻,请你负责将 n 个廊桥分配给国内区和国际区,使停靠廊桥的飞机数量最多。

输入格式

输入的第一行,包含三个正整数 n,m1​,m2​,分别表示廊桥的个数、国内航班飞机的数量、国际航班飞机的数量。

接下来m1​ 行,是国内航班的信息,第 i 行包含两个正整数 a1,i​,b1,i​,分别表示一架国内航班飞机的抵达、离开时刻。

接下来m2​ 行,是国际航班的信息,第 i 行包含两个正整数a2,i​,b2,i​,分别表示一架国际航班飞机的抵达、离开时刻。

每行的多个整数由空格分隔。

输出格式

输出一个正整数,表示能够停靠廊桥的飞机数量的最大值。

输入输出样例


输入 #1

复制

3 5 4
1 5
3 8
6 10
9 14
13 18
2 11
4 15
7 17
12 16


输出 #1

复制

7


输入 #2

复制

2 4 6
20 30
40 50
21 22
41 42
1 19
2 18
3 4
5 6
7 8
9 10


输出 #2

复制

4


输入 #3

复制

见附件中的 airport/airport3.in


输出 #3

复制

见附件中的 airport/airport3.ans

说明/提示


【样例解释 #1】

在图中,我们用抵达、离开时刻的数对来代表一架飞机,如 (1,5) 表示时刻 1 抵达、时刻 5 离开的飞机;用 √ 表示该飞机停靠在廊桥,用 × 表示该飞机停靠在远机位。

我们以表格中阴影部分的计算方式为例,说明该表的含义。在这一部分中,国际区有 22 个廊桥,44 架国际航班飞机依如下次序抵达:

  1. 首先 (2, 11) 在时刻 2 抵达,停靠在廊桥。
  2. 然后 (4, 15) 在时刻 4 抵达,停靠在另一个廊桥。
  3. 接着 (7, 17)在时刻 7 抵达,这时前 22 架飞机都还没离开、都还占用着廊桥,而国际区只有 22 个廊桥,所以只能停靠远机位。
  4. 最后 (12, 16)在时刻 12 抵达,这时 (2, 11) 这架飞机已经离开,所以有 1 个空闲的廊桥,该飞机可以停靠在廊桥。

根据表格中的计算结果,当国内区分配 2 个廊桥、国际区分配 1 个廊桥时,停靠廊桥的飞机数量最多,一共 7架。


【样例解释 #2】

当国内区分配 2 个廊桥、国际区分配 0 个廊桥时,停靠廊桥的飞机数量最多,一共 4 架,即所有的国内航班飞机都能停靠在廊桥。

需要注意的是,本题中廊桥的使用遵循“先到先得”的原则,如果国际区只有 1 个廊桥,那么将被飞机 (1,19) 占用,而不会被 (3,4)、(5,6)、(7,8)、(9,10) 这 4 架飞机先后使用。


题解:


设飞机降落的时间为 Start, 离开廊桥的时间为 Leave

首先要设定两个数组,分别记录国内和国外航班。考虑到题目要求是飞机先到先得,那么自然而然就想到,要按Start的大小递增排序。因为STL中的set容器有自动排列顺序的功能,我们可以这样来保存数据:

1. 定义一个结构体plane,保存Start和Leave

struct plane{
	int start;
	int leave;
};

2.定义一个plane类型的容器line,保存廊桥内停靠的飞机。

另外定义两个plane类型的容器inside,outside,分别表示国内国外飞机依次来临的顺序

另外为了访问set中的元素,同时为plane类型定义一个迭代器(指针)

set <plane> line;
set <plane> inside;
set <plane> outside;
set <plane>::iterator it;

3.因为我们定义的set容器是按照start的大小递增排序的,而struct的结构体中却有两个变量,我们要明确比较哪个变量。

bool operator <(const plane a,const plane b){
	return a.start<b.start;
}   //这个函数一定要放在struct外面,否则会报错

现在我们来思考核心算法

方案一(朴素):

思路:(贪心)

先考虑国内:

假设共有N个廊桥,从1~N依次枚举当廊桥数为 i 时,国内最多能停靠多少架飞机。那么怎样计算当廊桥数为 i 时,最多能停靠多少架飞机呢?(如下)

在国内廊桥数为 i 的情况下,设Ans表示最多能停的飞机数量

当一架飞机Z来临时,首先要更新廊桥内飞机的数量,对于Leave小于Z.Start(已经在之前飞走了)的飞机,那么要把它从Line中删去,遍历完Line所有飞机后再判断廊桥中是否有空闲的地方,即判断Line.size()< i ,若成立则可以把飞机Z加入到Line队列中,Ans++;

这样一趟下来,Ans的结果为廊桥数为 i 情况下的最多能停靠的飞机的数量

重复上述过程,我们可以定义一个数组Ansin[]来保存Ansin[i]的Ans;

同理对于国外飞机一样,重复上述过程,定义一个数组Ansout[]来保存。

可以发现,最后要求的结果为:


MAX=Ansin[i]+Ansout[N-i](i=0~N)

遍历一遍i值即可。

这个方法效率较低,考场时我用的这个算法(头脑简单),挂了很多分!

想看代码的在文章的最后,有2~3个洛谷测试的代码(35,50,55分),我感觉还可以再优化一番,很有提升空间,如果能给我提出建议,我会非常感激。

下面我们重点讲解另一种方法

方案二(找规律优化):


方案一最大的败笔为进行了很多冗余的运算,比如在枚举i时inside中第一架飞机被模拟了好几次。


所以,我们要想办法把前后的模拟建立起联系,前面建立起的数据能用就尽量用,而不再重复计算。

之前,我们一直限制廊桥的数量i来依次枚举,方案二我们就不再限制,假定飞机场内的廊桥数量为N,但我们给它编上号,1,2.3…i…N 规定飞机来时优先选择编号较小的位置。

我们拿一组数据模拟一下:(只考虑国内)

共有3个廊桥,5架飞机    (x,y)表示飞机的到来时间x,离开时间y

(1 , 12) (13 , 18) (3 , 8)  (6 , 15)  (4 , 5)

针对x排序

(1,12) (3,8) (4,5) (6,15) (13,18)

1号空余                                                                    (1,12)会停在1号

1号满,2号空余                                                        (3,8)会停在2号

1,2号满 ,三号空余                                                  (4,5)会停在3号

1,2号满,三号(4,5)已经离开,3号空余                    (6,15)会停在3号

1号已离开,一号空余,优先选择1                           (13,19)会停在1号

图解:

实际上上述过程可以描述为规定廊桥数量为1,第一轮筛选把能够停在廊桥的飞机记录数量s并删去,剩下的飞机再组成一个集合,把能够停在廊桥的飞机记录数量s并删去……….对于国外同理

可以发现:


Ansin[i]=Ansin[i-1]+s


时间复杂度O(nlogn)


Over….


Code(优化):

#include<cstdio>
#include<set>
using namespace std;
struct plane{
	int start;
	int leave;
};
inline int read(){
	char c=getchar();
	int t=0;
	while(c>='0' && c<='9'){
		t=(t<<1)+(t<<3)+(c^48);
		c=getchar();
	}
	return t;
}
bool operator <(const plane a,const plane b){
	return a.start<b.start;
}
inline void write(int num){
	if(num!=0){
		write(num/10);
		putchar((num%10)^48);
	}
}
set <plane> line;
set <plane>::iterator it;
int tot,in,out,res=0,tstart,s;
int ansin[100005];
int ansout[100005];
plane t;
signed main(){
	tot=read(),in=read(),out=read();
	ansin[0]=0,ansout[0]=0;
	for(int i=0;i<in;i++){
		t.start=read(),t.leave=read(),line.insert(t);
	}
	for(int i=1;i<=tot;i++){
		tstart=0;
		s=0;
		while(1){
			it=line.lower_bound(plane{tstart,0});
			if(it==line.end())break;
			tstart=(*it).leave;  
			line.erase(it);
			s++;
		}
		ansin[i]=ansin[i-1]+s;
	}
	line.clear();
	for(int i=0;i<out;i++){
		t.start=read(),t.leave=read(),line.insert(t); 
	}
	for(int i=1;i<=tot;i++){
		tstart=0;
		s=0;
		while(1){
			it=line.lower_bound(plane{tstart,0});
			if(it==line.end())break;
			tstart=(*it).leave;  
			line.erase(it);
			s++;
		}
		ansout[i]=ansout[i-1]+s;
	}
	for(int i=0;i<=tot;i++){
		if(res<ansin[i]+ansout[tot-i]){
			res=ansin[i]+ansout[tot-i];
		}
	}
	write(res);
	return 0;
}

Tip:不知道为什么洛谷21~23过不了,花费了一次机会自己跑了一遍数据,发现和答案一但还是判错,无语了~~瞬间佛系了

Code(不要喷我^_O_^)(35)(后面还有)

#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
int tot,inside,outside;
int neians[10001];
int waians[10001];
int ans=0;
struct plane{
	int start;
	int leave;
};
bool operator <(const plane a,const plane b){
		return a.start<b.start;
}
set<plane> guonei;
set<plane> guowai;
set<plane> line;
set<plane>::iterator itline;
inline int read(){
	char c;
	c=getchar();
	int ans=0;
	while(c>='0' && c<='9'){
		ans=(ans<<1)+(ans<<3)+(c^48);
		c=getchar();
	}
	return ans;
}
int main(){
	neians[0]=0;
	waians[0]=0;
	plane x;
	tot=read();
	inside=read();
	outside=read();
	for(int i=1;i<=inside;i++){
	    x.start=read();
	    x.leave=read();
        guonei.insert(x);
	}
	for(int i=1;i<=outside;i++){
		x.start=read();
		x.leave=read();
		guowai.insert(x);
	}
    for(int i=1;i<=tot;i++){
    	line.clear();
    	int aim=0;
        set <plane>::iterator it;
        for(it=guonei.begin();it!=guonei.end();it++){
        	if(line.size()<i){
        		line.insert((*it));
        		aim++;
			}
			else{
				set <plane>::iterator other;
				for(other=line.begin();other!=line.end();other++){
					if((*other).leave<(*it).start){
					     line.erase(other);
					     line.insert((*it));
					     aim++;
					     break;
					}
				}
			}
		}
		neians[i]=aim;
	}
	//
	for(int i=1;i<=tot;i++){
		line.clear();
    	int aim=0;
        set <plane>::iterator it;
        for(it=guowai.begin();it!=guowai.end();it++){
        	if(line.size()<i){
        		line.insert((*it));
        		aim++;
			}
			else{
				set <plane>::iterator other;
				for(other=line.begin();other!=line.end();other++){
					if((*other).leave<(*it).start){
					     line.erase(other);
					     line.insert((*it));
					     aim++;
					     break;
					}
				}
			}
		}
		waians[i]=aim;
	}
	for(int i=0;i<=tot;i++){
		if(ans<neians[i]+waians[tot-i]){
			ans=neians[i]+waians[tot-i];
		}
	}
	cout<<ans;
	return 0;
} 


Code(55)(我也服了我自己)

#include<cstdio>
#include<set>
using namespace std;
struct plane{
	int start;
	int leave;
};
bool operator <(const plane a,const plane b){
	return a.start<b.start;
}
set <plane> date;
set <plane> far;
plane line;
int ans=0;
int neians[80000];
int waians[80000];
inline int read(){
	char c=getchar();
	int t=0;
	while(c>='0' && c<='9'){
		t=(t<<1)+(t<<3)+(c^48);
		c=getchar();
	}
	return t;
}
inline void write(int num){
	if(num!=0){
		write(num/10);
		putchar((num%10)^48);
	}
}
int tot,in,out;
plane x;
int main(){
	neians[0]=0;
	waians[0]=0;
	set <plane>::iterator it;
	tot=read();
	in=read();
	out=read();
	for(int i=1;i<=in;i++){
		x.start=read();
		x.leave=read();
		date.insert(x);
	}
	bool choose=true; 
	for(int i=1;i<=tot;i++){
		int aim=0;
		if(choose){
			if(!date.empty()){
	    		line=*date.begin();
		    	date.erase(date.begin());
	    		it=date.begin(); 
	    		aim++;
		    	while(1){
		    		if(it==date.end())
			         	break;
			    	if((*it).start>=line.leave){
				    	aim++;
				    	line=*it;
					}
			    	else{
				    	far.insert(*it);
					}
		        	it++;
		    	}
		    	date.clear(); 
		    	choose=false;
			}
		}
		///
		else{
			if(!far.empty()){
	    	    line=*far.begin();
		    	far.erase(far.begin());
	    		it=far.begin(); 
	    		aim++;
		    	while(1){
		        	if(it==far.end())
			        	break;
			    	if((*it).start>=line.leave){
				   	 	aim++;
				    	line=*it;
			    	}
			    	else{
				    	date.insert(*it);
			   		}
		       	 	it++;
		    	}
		         far.clear();
		         choose=true;
		    }
		}
		neians[i]=aim+neians[i-1];
	}
	date.clear();
	far.clear();
	
	
	for(int i=1;i<=out;i++){
		x.start=read();
		x.leave=read();
		date.insert(x);
	} 
    choose=true; 
	for(int i=1;i<=tot;i++){
		int aim=0;
		if(choose){
			if(!date.empty()){
	    		line=*date.begin();
		    	date.erase(date.begin());
	    		it=date.begin(); 
	    		aim++;
		    	while(1){
		    		if(it==date.end())
			         	break;
			    	if((*it).start>=line.leave){
				    	aim++;
				    	line=*it;
					}
			    	else{
				    	far.insert(*it);
					}
		        	it++;
		    	}
		    	date.clear(); 
		    	choose=false;
			}
		}
		///
		else{
			if(!far.empty()){
	    	    line=*far.begin();
		    	far.erase(far.begin());
	    		it=far.begin(); 
	    		aim++;
		    	while(1){
		        	if(it==far.end())
			        	break;
			    	if((*it).start>=line.leave){
				   	 	aim++;
				    	line=*it;
			    	}
			    	else{
				    	date.insert(*it);
			   		}
		       	 	it++;
		    	}
		         far.clear();
		         choose=true;
		    }
		}
		waians[i]=aim+waians[i-1];
	}
	for(int i=0;i<=tot;i++){
		if(ans<neians[i]+waians[tot-i]){
			ans=neians[i]+waians[tot-i];
		}
	}
	write(ans);
	return 0;
} 

Gift For you


送你一幅“清明上河图”

End~~~


写作很不容易,谢谢大家支持



版权声明:本文为way_back原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。