总目录详见:NOIP 提高组 复赛 试题 目录 信奥 历年
在线测评地址:洛谷 P7913 [CSP-S 2021] 廊桥分配
1.20分(纯暴力)
对于 20% 的数据,n≤100,m1+m2≤100
所有a1,i,b1,i,a2,i,b2,i 为数值不超过 10^8 的互不相同的正整数,且保证对于每个 i∈[1,m1],都有 a1,i<b1,i,以及对于每个 i∈[1,m2],都有 a2,i<b2,i
关键字:先到先得。若国际区有空余,有国际航班到站,无论该航班停靠多长时间,必须给予停靠。
数据量不大,n占用第一层循环,m1,m2均占用第二层循环,程序的最多计算次数100*100=10000,完全不会超时。
因飞机有抵达和飞离时间,需要动用结构体。
现给定未来一段时间飞机的抵达、离开时刻,没有说明按什么顺序抵达,稳妥起见,还是要进行抵达时间的排序。
有一个疑问,抵达飞机和飞离飞机,能同时发生吗?(整个题目看完,才发现,这不可能,题中说:所有a1,i,b1,i,a2,i,b2,i 为数值不超过 10^8 的互不相同的正整数)
评估算法的时间复杂度O(n*(m1+m2)*n*log(n)),即100*100*100*2=2*10^6,不会超时。
20分代码如下:
#include <bits/stdc++.h>
#define maxn 110
using namespace std;
struct node{
int a,b;
}gn[maxn],gj[maxn],gn2[maxn],gj2[maxn];
bool cmp(node a,node b){
return a.a<b.a;//按抵达时间,自小到大排序
}
bool cmp2(node a,node b){
return a.b>b.b;//按飞离时间,自大到小排序
}
int main(){
int n,m1,m2,n1,n2,i,j,cnt1=0,cnt2=0,cnt=0,mx=0;
scanf("%d%d%d",&n,&m1,&m2);
for(i=1;i<=m1;i++)
scanf("%d%d",&gn[i].a,&gn[i].b);
sort(gn+1,gn+1+m1,cmp);
for(i=1;i<=m2;i++)
scanf("%d%d",&gj[i].a,&gj[i].b);
sort(gj+1,gj+1+m2,cmp);
for(n1=0;n1<=n;n1++){
n2=n-n1;
cnt=0,cnt1=0,cnt2=0;
if(n1){
for(i=1;i<=m1;i++){//m1,m2是对称的代码
if(cnt1<n1){//还有空余
cnt1++,cnt++;
gn2[cnt1]=gn[i];
}
else{//停满
sort(gn2+1,gn2+1+cnt1,cmp2);
while(cnt1&&gn2[cnt1].b<gn[i].a)cnt1--;
if(cnt1<n1){//有空闲,继续加入
cnt1++,cnt++;
gn2[cnt1]=gn[i];
}
}
}
}
if(n2){
for(i=1;i<=m2;i++){//m1,m2是对称的代码
if(cnt2<n2){
cnt2++,cnt++;
gj2[cnt2]=gj[i];
}
else{
sort(gj2+1,gj2+1+cnt2,cmp2);
while(cnt2&&gj2[cnt2].b<gj[i].a)cnt2--;
if(cnt2<n2){//有空闲,继续加入
cnt2++,cnt++;
gj2[cnt2]=gj[i];
}
}
}
}
mx=max(mx,cnt);
}
printf("%d\n",mx);
return 0;
}
极限能到25分
#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
struct node{
int a,b;
}gn[maxn],gj[maxn],gn2[maxn],gj2[maxn];
bool cmp(node a,node b){
return a.a<b.a;//按抵达时间,自小到大排序
}
bool cmp2(node a,node b){
return a.b>b.b;//按飞离时间,自大到小排序
}
int main(){
int n,m1,m2,n1,n2,i,j,cnt1=0,cnt2=0,cnt=0,mx=0;
scanf("%d%d%d",&n,&m1,&m2);
for(i=1;i<=m1;i++)
scanf("%d%d",&gn[i].a,&gn[i].b);
sort(gn+1,gn+1+m1,cmp);
for(i=1;i<=m2;i++)
scanf("%d%d",&gj[i].a,&gj[i].b);
sort(gj+1,gj+1+m2,cmp);
for(n1=0;n1<=n;n1++){
n2=n-n1;
cnt=0,cnt1=0,cnt2=0;
if(n1){
for(i=1;i<=m1;i++){//m1,m2是对称的代码
if(cnt1<n1){//还有空余
cnt1++,cnt++;
gn2[cnt1]=gn[i];
}
else{//停满
sort(gn2+1,gn2+1+cnt1,cmp2);
while(cnt1&&gn2[cnt1].b<gn[i].a)cnt1--;
if(cnt1<n1){//有空闲,继续加入
cnt1++,cnt++;
gn2[cnt1]=gn[i];
}
}
}
}
if(n2){
for(i=1;i<=m2;i++){//m1,m2是对称的代码
if(cnt2<n2){
cnt2++,cnt++;
gj2[cnt2]=gj[i];
}
else{
sort(gj2+1,gj2+1+cnt2,cmp2);
while(cnt2&&gj2[cnt2].b<gj[i].a)cnt2--;
if(cnt2<n2){//有空闲,继续加入
cnt2++,cnt++;
gj2[cnt2]=gj[i];
}
}
}
}
mx=max(mx,cnt);
}
printf("%d\n",mx);
return 0;
}
还是有惊喜的,多得了5分。
2.40分
对于 40%的数据,n≤5000,m1+m2≤5000
所有a1,i,b1,i,a2,i,b2,i 为数值不超过 10^8 的互不相同的正整数,且保证对于每个 i∈[1,m1],都有 a1,i<b1,i,以及对于每个 i∈[1,m2],都有 a2,i<b2,i
对纯暴力进行优化,用优先队列存储停放在廊桥的飞机,评估算法的时间复杂度O(n*(m1+m2)*log(n)),即5000*5000*log(5000)=2.5*10^7*4=10^8,大概率超时,不过可以试一试。
40分代码如下:
#include <bits/stdc++.h>
#define maxn 5010
using namespace std;
struct node{
int a,b;
}gn[maxn],gj[maxn];
struct node2{
int a,b;
friend bool operator < (const node2 &nd1,const node2 &nd2){
return nd1.b>nd2.b;//按飞离时间,自小到大排序
}
}hb;
bool cmp(node a,node b){
return a.a<b.a;//按抵达时间,自小到大排序
}
int main(){
int n,m1,m2,n1,n2,i,j,cnt1=0,cnt2=0,cnt=0,mx=0,a,b;
scanf("%d%d%d",&n,&m1,&m2);
for(i=1;i<=m1;i++)
scanf("%d%d",&gn[i].a,&gn[i].b);
sort(gn+1,gn+1+m1,cmp);
for(i=1;i<=m2;i++)
scanf("%d%d",&gj[i].a,&gj[i].b);
sort(gj+1,gj+1+m2,cmp);
for(n1=0;n1<=n;n1++){
n2=n-n1;
cnt=0;
priority_queue<node2> gn2,gj2;
if(n1){
for(i=1;i<=m1;i++){//m1,m2是对称的代码
if(gn2.empty()!=true){//尝试腾出廊桥
b=gn2.top().b;
if(b<gn[i].a)
gn2.pop();
}
if(gn2.size()<n1){//还有空余
cnt++;
hb.a=gn[i].a;
hb.b=gn[i].b;
gn2.push(hb);//用堆减少计算次数
}
}
}
if(n2){
for(i=1;i<=m2;i++){//m1,m2是对称的代码
if(gj2.empty()!=true){//尝试腾出廊桥
b=gj2.top().b;
if(b<gj[i].a)
gj2.pop();
}
if(gj2.size()<n2){//还有空余
cnt++;
hb.a=gj[i].a;
hb.b=gj[i].b;
gj2.push(hb);//用堆减少计算次数
}
}
}
mx=max(mx,cnt);
}
printf("%d\n",mx);
return 0;
}
极限能到45分,满意
#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
struct node{
int a,b;
}gn[maxn],gj[maxn];
struct node2{
int a,b;
friend bool operator < (const node2 &nd1,const node2 &nd2){
return nd1.b>nd2.b;//按飞离时间,自小到大排序
}
}hb;
bool cmp(node a,node b){
return a.a<b.a;//按抵达时间,自小到大排序
}
int main(){
int n,m1,m2,n1,n2,i,j,cnt1=0,cnt2=0,cnt=0,mx=0,a,b;
scanf("%d%d%d",&n,&m1,&m2);
for(i=1;i<=m1;i++)
scanf("%d%d",&gn[i].a,&gn[i].b);
sort(gn+1,gn+1+m1,cmp);
for(i=1;i<=m2;i++)
scanf("%d%d",&gj[i].a,&gj[i].b);
sort(gj+1,gj+1+m2,cmp);
for(n1=0;n1<=n;n1++){
n2=n-n1;
cnt=0;
priority_queue<node2> gn2,gj2;
if(n1){
for(i=1;i<=m1;i++){//m1,m2是对称的代码
if(gn2.empty()!=true){//尝试腾出廊桥
b=gn2.top().b;
if(b<gn[i].a)
gn2.pop();
}
if(gn2.size()<n1){//还有空余
cnt++;
hb.a=gn[i].a;
hb.b=gn[i].b;
gn2.push(hb);//用堆减少计算次数
}
}
}
if(n2){
for(i=1;i<=m2;i++){//m1,m2是对称的代码
if(gj2.empty()!=true){//尝试腾出廊桥
b=gj2.top().b;
if(b<gj[i].a)
gj2.pop();
}
if(gj2.size()<n2){//还有空余
cnt++;
hb.a=gj[i].a;
hb.b=gj[i].b;
gj2.push(hb);//用堆减少计算次数
}
}
}
mx=max(mx,cnt);
}
printf("%d\n",mx);
return 0;
}
3.100分
对于 100%的数据,1≤n≤100000,1≤m1+m2≤100000.
所有a1,i,b1,i,a2,i,b2,i 为数值不超过 10^8 的互不相同的正整数,且保证对于每个 i∈[1,m1],都有 a1,i<b1,i,以及对于每个 i∈[1,m2],都有 a2,i<b2,i
猜测算法的时间复杂度是O(n*(logm1+logm2))
让我们先忽略廊桥数量的限制来安排航班。我们维护一个空闲的廊桥队列,每到达一架航班,就给它安排编号最小的廊桥供其使用。
现在加上廊桥数量的限制。容易发现刚才的廊桥分配方法直接就帮我们解决了廊桥限制的问题:如果当前有 n 个廊桥可供使用,则分配到 n+1 号及以后的廊桥实质上就是分配到远机位了,不需要再做任何额外的处理。
到这里做法就很清晰了:我们按照开头提到的分配方法来安排航班的停靠位置,记录各廊桥停靠的航班数,做一个前缀和,最后枚举分配给某个区的廊桥数,算出各情况下两区实际使用廊桥的航班数总和,即可解决本题。
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
typedef pair<int, int> pii;
struct range {
int x, y;
} a[100005], b[100005];
int res1[100005], res2[100005];
int n;
bool cmp(const range& a, const range& b) { return a.x < b.x; }
void calc(range* t, int m, int* res) {
priority_queue<pii, vector<pii>, greater<pii> > lq; // 等待离港航班队列
priority_queue<int, vector<int>, greater<int> > wq; // 空闲廊桥队列
for (int i = 1; i <= n; i++) wq.push(i);
for (int i = 1; i <= m; i++) {
while (!lq.empty() && t[i].x >= lq.top().first) {
wq.push(lq.top().second);
lq.pop();
}
if (wq.empty()) continue;
int dest = wq.top();
wq.pop();
res[dest]++;
lq.push(make_pair(t[i].y, dest));
}
for (int i = 1; i <= n; i++) res[i] += res[i - 1];
}
int main() {
int m1, m2;
cin >> n >> m1 >> m2;
for (int i = 1; i <= m1; i++) cin >> a[i].x >> a[i].y;
for (int i = 1; i <= m2; i++) cin >> b[i].x >> b[i].y;
sort(a + 1, a + m1 + 1, cmp);
sort(b + 1, b + m2 + 1, cmp);
calc(a, m1, res1);
calc(b, m2, res2);
int ans = 0;
for (int i = 0; i <= n; i++) {
ans = max(ans, res1[i] + res2[n - i]);
}
cout << ans << endl;
return 0;
}