洛谷 P7913 [CSP-S 2021] 廊桥分配

PS:如果读过题了可以跳过题目描述直接到题解部分
提交链接:洛谷 P7913 [CSP-S 2021] 廊桥分配

[CSP-S 2021] 廊桥分配

题目描述

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

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

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

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

输入格式

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

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

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

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

输出格式

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

样例 #1

样例输入 #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

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

样例输入 #3

10 100 100
13 956
463 517
281 586
540 636
103 573
890 893
45 639
23 320
305 667
556 775
630 716
529 1000
173 741
174 276
6 51
724 763
291 663
334 401
250 511
373 710
467 696
265 449
317 432
92 955
14 707
411 860
603 643
251 874
190 705
9 310
285 539
408 615
861 951
319 413
368 714
264 688
271 670
43 353
792 872
240 770
2 348
325 687
253 750
464 509
543 704
963 989
4 998
148 198
698 899
532 929
50 149
41 168
255 802
246 768
252 656
237 363
146 151
70 119
364 477
578 936
771 809
551 952
434 903
535 609
607 948
446 828
311 313
758 937
62 122
11 614
909 947
898 918
201 862
178 421
176 269
38 420
513 754
67 175
254 360
740 912
134 225
141 922
87 111
553 751
234 331
329 452
783 810
55 162
136 322
762 977
387 856
314 815
653 935
442 817
36 212
362 949
30 637
737 832
53 999
159 531
431 796
215 385
63 718
395 647
289 298
488 545
7 438
596 876
611 628
1 699
61 278
286 367
196 220
25 645
772 914
323 328
537 984
465 501
445 672
19 709
581 953
126 550
88 326
969 994
184 245
247 346
284 660
339 407
338 584
599 703
199 224
959 974
365 826
496 519
34 980
73 835
624 712
171 209
419 466
40 934
189 476
559 646
577 804
816 821
376 435
392 572
793 852
306 495
457 593
206 340
127 512
161 260
273 855
669 897
95 892
824 849
72 524
48 86
576 585
5 927
487 561
676 819
107 734
143 781
642 674
28 129
514 695
137 294
94 978
390 957
147 261
482 652
12 403
571 941
399 961
681 789
83 181
230 853
564 764
542 732
394 654
283 827
106 790
486 993
56 480
105 931
910 991
379 469
479 689
104 414
612 800
798 850
785 799
185 337
15 973
791 807
610 728
396 923
277 884
69 79

样例输出 #3

32

提示

【样例解释 #1】

洛谷 P7913 [CSP-S 2021] 廊桥分配_第1张图片

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

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

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

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

【样例解释 #2】

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

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

【数据范围】

对于 20 % 20 \% 20% 的数据, n ≤ 100 n \le 100 n100 m 1 + m 2 ≤ 100 m_1 + m_2 \le 100 m1+m2100
对于 40 % 40 \% 40% 的数据, n ≤ 5000 n \le 5000 n5000 m 1 + m 2 ≤ 5000 m_1 + m_2 \le 5000 m1+m25000
对于 100 % 100 \% 100% 的数据, 1 ≤ n ≤ 10 5 1 \le n \le {10}^5 1n105 m 1 , m 2 ≥ 1 m_1, m_2 \ge 1 m1,m21 m 1 + m 2 ≤ 10 5 m_1 + m_2 \le {10}^5 m1+m2105,所有 a 1 , i , b 1 , i , a 2 , i , b 2 , i a_{1, i}, b_{1, i}, a_{2, i}, b_{2, i} a1,i,b1,i,a2,i,b2,i 为数值不超过 10 8 {10}^8 108 的互不相同的正整数,且保证对于每个 i ∈ [ 1 , m 1 ] i \in [1, m_1] i[1,m1],都有 a 1 , i < b 1 , i a_{1, i} < b_{1, i} a1,i<b1,i,以及对于每个 i ∈ [ 1 , m 2 ] i \in [1, m_2] i[1,m2],都有 a 2 , i < b 2 , i a_{2, i} < b_{2, i} a2,i<b2,i

【感谢 hack 数据提供】

  • xingxuxin。
  • cyslngsul。

题解

45pts

先讲一个比较容易想到的错误的骗分思路。
用贪心的方式分配廊桥,代码实现非常显然,直接看代码就能懂,我就不赘述了。

90pts

45pts贪心的错误原因其实是很容易发现的:假设现在只剩最后两个廊桥,国内的廊桥有5个飞机,国际有3个飞机,这时我们会优先选择把廊桥分给国内的,而如果国际的下一个廊桥有15个飞机,这时我们就会发现,我们的选择就不是最优的了。

所以我们可以换一种方式,给每个廊桥的飞机数求前缀和,然后循环一个 i i i表示给国内分配的廊桥数,剩下的都给国际,求最大值就好了。

100pts

90pts的贪心会有一个点是时间超限,这是显然的,毕竟是一个大概 O ( n 2 ) O(n^2) O(n2)的算法(我不知道我算的对不对),但还有两个点WA掉(实话说我不知道是为什么),但把cnt1和cnt2的循环改成n就对了。

但Subtask #1还是有一个点会T,不过没有计分。

100pts最终版

因为班上大佬出的数据过于冥间,我又被迫回来继续改那个T掉的点。

通过浏览一些题解,我发现了一个比较好理解的方法来解决T掉的问题,就是用二分查找+路径压缩来对飞机进行分组。

这是原题解题解 P7913 [CSP-S 2021] 廊桥分配。

代码实现

45pts

//洛谷 P7913 [CSP-S 2021] 廊桥分配
#pragma GCC optimize(3)
#include
#include
#include
using namespace std;
int n,m1,m2;
int cnt1=1,cnt2=1;
int x1[100010],x2[100010];
int t1[100010],t2[100010];
int ans;

struct plane{
	int a,b;
}x[100010];

void in(int &x){
	int nt;
	x=0;
	while(!isdigit(nt=getchar()));
	x=nt^'0';
	while(isdigit(nt=getchar())){
		x=(x<<3)+(x<<1)+(nt^'0');
	}
}

bool cmp(plane x,plane y){
	return x.a<y.a;
}

int main(){
	register int i,j;
	in(n),in(m1),in(m2);
	for(i=1;i<=m1;++i){
		in(x[i].a),in(x[i].b);
	}
	sort(x+1,x+m1+1,cmp);
	for(i=1;i<=m1;++i){
		for(j=1;j<=cnt1;++j){
			if(t1[j]<=x[i].a){
				++x1[j];
				t1[j]=x[i].b;
				break;
			}
		}
		if(j==cnt1){
			++cnt1;
		}
	}
	for(i=1;i<=m2;++i){
		in(x[i].a),in(x[i].b);
	}
	sort(x+1,x+m2+1,cmp);
	for(i=1;i<=m2;++i){
		for(j=1;j<=cnt2;++j){
			if(t2[j]<=x[i].a){
				++x2[j];
				t2[j]=x[i].b;
				break;
			}
		}
		if(j==cnt2){
			++cnt2;
		}
	}
	for(i=1,j=1;i<=cnt1&&j<=cnt2&&i+j<=n+1;){
		if(x1[i]>=x2[j]){
			ans+=x1[i];
			++i;
		}
		else{
			ans+=x2[j];
			++j;
		}
	}
	printf("%d\n",ans);
	return 0;
}

90pts

//洛谷 P7913 [CSP-S 2021] 廊桥分配
#pragma GCC optimize(3)
#include
#include
#include
using namespace std;
int n,m1,m2;
int cnt1=1,cnt2=1;
int x1[100010],x2[100010];
int t1[100010],t2[100010];
int ans;

struct plane{
	int a,b;
}x[100010];

void in(int &x){
	int nt;
	x=0;
	while(!isdigit(nt=getchar()));
	x=nt^'0';
	while(isdigit(nt=getchar())){
		x=(x<<3)+(x<<1)+(nt^'0');
	}
}

bool cmp(plane x,plane y){
	return x.a<y.a;
}

int main(){
	register int i,j;
	in(n),in(m1),in(m2);
	for(i=1;i<=m1;++i){
		in(x[i].a),in(x[i].b);
	}
	sort(x+1,x+m1+1,cmp);
	for(i=1;i<=m1;++i){
		for(j=1;j<=cnt1;++j){
			if(t1[j]<=x[i].a){
				++x1[j];
				t1[j]=x[i].b;
				break;
			}
		}
		if(j==cnt1){
			++cnt1;
		}
	}
	for(i=1;i<=m2;++i){
		in(x[i].a),in(x[i].b);
	}
	sort(x+1,x+m2+1,cmp);
	for(i=1;i<=m2;++i){
		for(j=1;j<=cnt2;++j){
			if(t2[j]<=x[i].a){
				++x2[j];
				t2[j]=x[i].b;
				break;
			}
		}
		if(j==cnt2){
			++cnt2;
		}
	}
	for(i=1;i<=cnt1&&i<=n;++i){
		x1[i]+=x1[i-1];
	}
	for(i=1;i<=cnt2&&i<=n;++i){
		x2[i]+=x2[i-1];
	}
	for(i=0;i<=n;++i){
		ans=max(ans,x1[i]+x2[n-i]);
	}
	printf("%d\n",ans);
	return 0;
}

100pts(Subtask #1有一个点会T)

//洛谷 P7913 [CSP-S 2021] 廊桥分配
#pragma GCC optimize(3)
#include
#include
#include
using namespace std;
int n,m1,m2;
int x1[100010],x2[100010];
int t1[100010],t2[100010];
int ans;

struct plane{
	int a,b;
}x[100010];

void in(int &x){
	int nt;
	x=0;
	while(!isdigit(nt=getchar()));
	x=nt^'0';
	while(isdigit(nt=getchar())){
		x=(x<<3)+(x<<1)+(nt^'0');
	}
}

bool cmp(plane x,plane y){
	return x.a<y.a;
}

int main(){
	register int i,j;
	in(n),in(m1),in(m2);
	for(i=1;i<=m1;++i){
		in(x[i].a),in(x[i].b);
	}
	sort(x+1,x+m1+1,cmp);
	for(i=1;i<=m1;++i){
		for(j=1;j<=n;++j){
			if(t1[j]<=x[i].a){
				++x1[j];
				t1[j]=x[i].b;
				break;
			}
		}
	}
	for(i=1;i<=m2;++i){
		in(x[i].a),in(x[i].b);
	}
	sort(x+1,x+m2+1,cmp);
	for(i=1;i<=m2;++i){
		for(j=1;j<=n;++j){
			if(t2[j]<=x[i].a){
				++x2[j];
				t2[j]=x[i].b;
				break;
			}
		}
	}
	for(i=1;i<=n;++i){
		x1[i]+=x1[i-1];
	}
	for(i=1;i<=n;++i){
		x2[i]+=x2[i-1];
	}
	for(i=0;i<=n;++i){
		ans=max(ans,x1[i]+x2[n-i]);
	}
	printf("%d\n",ans);
	return 0;
}

100pts最终版

//洛谷 P7913 [CSP-S 2021] 廊桥分配
#pragma GCC optimize(3)
#include
#include
#include
#include
#include
using namespace std;
int n,m1,m2;
int cnt=1;
int cnt1,cnt2;
int x1[100010],x2[100010];
int c[100010];
int ans;

struct plane{
	int a,b,id;
}x[100010];

void in(int &x){
	int nt;
	x=0;
	while(!isdigit(nt=getchar()));
	x=nt^'0';
	while(isdigit(nt=getchar())){
		x=(x<<3)+(x<<1)+(nt^'0');
	}
}

bool cmp(plane x,plane y){
	return x.a<y.a;
}

int find(int i,int m){
	int l=1,r=m,res=-1;
	while(l<=r){
		int mid=l+r>>1;
		if(x[mid].a>=i){
			res=mid;
			r=mid-1;
		}
		else{
			l=mid+1;
		}
	}
	if(res<0){
		return res;
	}
	vector<int>v;
	while(res>0&&x[res].id!=0){
		v.push_back(res);
		res=c[res];
	}
	while(!v.empty()){
		c[v.back()]=c[res];
		v.pop_back();
	}
	return res;
}

int main(){
	register int i,j,k;
	in(n),in(m1),in(m2);
	for(i=1;i<=m1;++i){
		in(x[i].a),in(x[i].b);
	}
	sort(x+1,x+m1+1,cmp);
	for(i=1;i<m1;++i){
		c[i]=i+1;
	}
	c[m1]=-1;
	for(i=1;i<=m1;++i){
		if(x[i].id!=0){
			continue;
		}
		x[i].id=1;
		cnt=1;
		j=x[i].b+1;
		k=find(j,m1);
		while(k>0){
			x[k].id=1;
			j=x[k].b+1;
			++cnt;
			k=find(j,m1);
		}
		++cnt1;
		x1[cnt1]=cnt;
	}
	memset(x,0,sizeof(x));
	memset(c,0,sizeof(c));
	for(i=1;i<=m2;++i){
		in(x[i].a),in(x[i].b);
	}
	sort(x+1,x+m2+1,cmp);
	for(i=1;i<m2;++i){
		c[i]=i+1;
	}
	c[m2]=-1;
	for(i=1;i<=m2;++i){
		if(x[i].id!=0){
			continue;
		}
		x[i].id=1;
		cnt=1;
		j=x[i].b+1;
		k=find(j,m2);
		while(k>0){
			x[k].id=1;
			j=x[k].b+1;
			++cnt;
			k=find(j,m2);
		}
		++cnt2;
		x2[cnt2]=cnt;
	}
	for(i=1;i<=n;++i){
		x1[i]+=x1[i-1];
	}
	for(i=1;i<=n;++i){
		x2[i]+=x2[i-1];
	}
	for(i=0;i<=n;++i){
		ans=max(ans,x1[i]+x2[n-i]);
	}
	printf("%d\n",ans);
	return 0;
}

你可能感兴趣的:(算法基础,数据结构,贪心算法,二分查找,并查集,路径压缩)