2023年天梯赛-全国总决赛

在这里插入图片描述

L2阶段题目(25分)

L2-045 堆宝塔

在这里插入图片描述

堆宝塔游戏是让小朋友根据抓到的彩虹圈的直径大小,按照从大到小的顺序堆起宝塔。但彩虹圈不一定是按照直径的大小顺序抓到的。聪明宝宝采取的策略如下:

  • 首先准备两根柱子,一根 A 柱串宝塔,一根 B 柱用于临时叠放。
  • 把第 1 块彩虹圈作为第 1 座宝塔的基座,在 A 柱放好。
    将抓到的下一块彩虹圈 C 跟当前 A 柱宝塔最上面的彩虹圈比一下,如果比最上面的小,就直接放上去;否则把 C 跟 B 柱最上面的彩虹圈比一下:
  •     如果 B 柱是空的、或者 C 大,就在 B 柱上放好;
  •     否则把 A 柱上串好的宝塔取下来作为一件成品;然后把 B 柱上所有比 C 大的彩虹圈逐一取下放到 A 柱上,最后把 C 也放到 A 柱上。

重复此步骤,直到所有的彩虹圈都被抓完。最后 A 柱上剩下的宝塔作为一件成品,B 柱上剩下的彩虹圈被逐一取下,堆成另一座宝塔。问:宝宝一共堆出了几个宝塔?最高的宝塔有多少层?

解题思路

用C++中的STL里面的stack然后按题意模拟即可。

参考代码

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using ld = long double;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
void solve(){
	stack<int> s1,s2;
	int n;
	cin >> n;
	int ans1 = 0,ans2 = 0;
	int x;
	cin >> x;
	s1.push(x);
	for(int i = 2;i <= n;i ++){
		int x;
		cin >> x;
		int t = s1.top();
		if(x < t){
			s1.push(x);
		}else{
			if(s2.empty()){
				s2.push(x);
			}else{
				int tt = s2.top();
				if(x > tt){
					s2.push(x);
				}else{
					int cnt = 0;
					while(!s1.empty()){
						s1.pop();
						cnt ++;
					}
					ans1 ++;
					ans2 = max(ans2,cnt);
					while(!s2.empty()){
						int ee = s2.top();
						if(ee > x){
							s1.push(ee);
						}else{
							break;
						}
						s2.pop();
					}
					s1.push(x);
				}
			}
		}
	}
	int cnt = 0;
	while(!s1.empty()){
		s1.pop();
		cnt ++;
	}
	if(cnt){
		ans1 ++;
		ans2 = max(ans2,cnt);
	}
	cnt = 0;
	while(!s2.empty()){
		s2.pop();
		cnt ++;
	}
	if(cnt){
		ans1 ++;
		ans2 = max(ans2,cnt);
	}
	cout << ans1 << " " << ans2;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t = 1;
//	cin >> t;
	
	while(t --){
		solve();
	}
}

L2-046 天梯赛的赛场安排

在这里插入图片描述

天梯赛使用 OMS 监考系统,需要将参赛队员安排到系统中的虚拟赛场里,并为每个赛场分配一位监考老师。每位监考老师需要联系自己赛场内队员对应的教练们,以便发放比赛账号。为了尽可能减少教练和监考的沟通负担,我们要求赛场的安排满足以下条件:

  • 每位监考老师负责的赛场里,队员人数不得超过赛场规定容量 C;
  • 每位教练需要联系的监考人数尽可能少 —— 这里假设每所参赛学校只有一位负责联系的教练,且每个赛场的监考老师都不相同。

为此我们设计了多轮次排座算法,按照尚未安排赛场的队员人数从大到小的顺序,每一轮对当前未安排的人数最多的学校进行处理。记当前待处理的学校未安排人数为 n:

  • 如果 n≥C,则新开一个赛场,将 C 位队员安排进去。剩下的人继续按人数规模排队,等待下一轮处理;
  • 如果 n<C,则寻找剩余空位数大于等于 n 的编号最小的赛场,将队员安排进去;
  • 如果 n<C,且找不到任何非空的、剩余空位数大于等于 n 的赛场了,则新开一个赛场,将队员安排进去。

由于近年来天梯赛的参赛人数快速增长,2023年超过了 480 所学校 1.6 万人,所以我们必须写个程序来处理赛场安排问题。

解题思路

使用优先队列priority_queue按学校的队员人数从大到小排序然后根据题目条件即可。
值得一提的是,优先队列该如何重载< 符号来定义排序规则。
首先我们假设这里的队员人数为count,按count从大到小排序结构体的话,就是要当a.count ? b.count 时使得结构体a < b就是我们要确定的符号(< 还是 >)。因为优先队列是按照优先级来排序的,优先级高的会在前面,所以这里应该是a.count < b.count 。意思就是count更小的结构体优先级更低会排在后面,那么count更大的优先级自然也就更高了排在前面了。那么这里的重载 < 参考写法如下:

bool operator <(const node& a)const{
	return count < a.count;
}

参考代码

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using ld = long double;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
struct node{
	int code;
	string name;
	int count;
	bool operator <(const node& a)const{
		return count < a.count;
	}
};
void solve(){
	priority_queue<node> q;
	int n,c;
	cin >> n >> c;
	vector<int> ans(n + 1);
	map<int,string> mp;
	int sum = 0;
	for(int i = 1;i <= n;i ++){
		string s;
		int k;
		cin >> s >> k;
		mp[i] = s;
		q.push({i,s,k});
	}
	vector<int> v;
	while(!q.empty()){
		auto tmp = q.top();
		q.pop();
		int res = tmp.count;
		if(res >= c){
			sum ++;
			ans[tmp.code] ++;
			res -= c;
			tmp.count = res;
			if(res){
				q.push(tmp);
			}		
		}else{
			int f = 0;
			for(int j = 0;j < v.size();j ++){
				if(res <= c - v[j]){
					v[j] += res;
					ans[tmp.code] ++;
					f = 1;
					break;
				}
			}
			if(!f){
				v.push_back(res);
				ans[tmp.code] ++;
				sum ++;
			}
		}
	}
	for(int i = 1;i <= n;i ++){
		cout << mp[i] << " " << ans[i] << "\n";
	}
	cout << sum << "\n";
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t = 1;
//	cin >> t;
	
	while(t --){
		solve();
	}
}

L2-047 锦标赛

2 k 2^k 2k 名选手将要参加一场锦标赛。锦标赛共有 k k k 轮,其中第 i i i 轮的比赛共有 2 k − i 2^{k−i} 2ki场,每场比赛恰有两名选手参加并从中产生一名胜者。每场比赛的安排如下:

  • 对于第 1 1 1 轮的第 j j j 场比赛,由第 ( 2 j − 1 ) (2j−1) (2j1) 名选手对抗第 2 j 2j 2j 名选手。
  • 对于第 i i i 轮的第 j 场比赛 ( i > 1 ) (i>1) i>1,由第 ( i − 1 ) (i−1) (i1) 轮第 ( 2 j − 1 ) (2j−1) (2j1) 场比赛的胜者对抗第 ( i − 1 ) (i−1) (i1) 轮第 2 j 2j 2j 场比赛的胜者。

k k k 轮唯一一场比赛的胜者就是整个锦标赛的最终胜者。
举个例子,假如共有 8 8 8 名选手参加锦标赛,则比赛的安排如下:

  • 1 1 1 轮共 4 4 4 场比赛:选手 1 1 1 vs 选手 2 2 2,选手 3 3 3 vs 选手 4 4 4,选手 5 5 5 vs 选手 6 6 6,选手 7 7 7 vs 选手 8 8 8
  • 2 2 2 轮共 2 2 2 场比赛:第 1 1 1 轮第 1 1 1 场的胜者 vs 第 1 1 1 轮第 2 2 2 场的胜者,第 1 1 1 轮第 3 3 3 场的胜者 vs 第 1 1 1 轮第 4 4 4 场的胜者。
  • 3 3 3 轮共 1 1 1 场比赛:第 2 2 2 轮第 1 1 1 场的胜者 vs 第 2 2 2 轮第 2 2 2 场的胜者。

已知每一名选手都有一个能力值,其中第 i i i 名选手的能力值为 a [ i ] a[i] a[i] 。在一场比赛中,若两名选手的能力值不同,则能力值较大的选手一定会打败能力值较小的选手;若两名选手的能力值相同,则两名选手都有可能成为胜者。

解题思路

这题分没拿满,直接按照题意大致模拟了一下,可以直接看下代码先,到时候再改下。

参考代码

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using ld = long double;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
void solve(){
	int k;
	cin >> k;
	vector<int> a((1 << k) + 1);
	queue<int> q;
	for(int i = 1;i <= (1 << (k - 1));i ++){
		int x;
		cin >> x;
		a[i * 2] = x;
		q.push(i * 2 - 1);
	}
	int f = 0;
	for(int i = 2;i <= k;i ++){
		for(int j = 1;j <= (1 << (k - i));j ++){
			int x;
			cin >> x;
			int k1 = q.front() + 1;
			q.pop();
			int k2 = q.front() + 1;
			q.pop();
			if(x <= a[k1] && x <= a[k2]){
//				cout << x << "\n";
//				cout << a[k1] << " " << a[k2] << "\n";
				f = 1;
				continue;
			}
			if(x >= a[k1] && x >= a[k2]){
				if(a[k1] > a[k2]){
					a[k1 - 1] = x;
					q.push(k2 - 1);
				}else{
					a[k2 - 1] = x;
					q.push(k1 - 1);
				}
				continue;
			}
			if(x >= a[k1]){
				a[k1 - 1] = x;
				q.push(k2 - 1);
				continue;
			}
			if(x >= a[k2]){
				a[k2 - 1] = x;
				q.push(k1 - 1);
				continue;
			}
		}
	}
	int w;
	cin >> w;
	if(f){
		cout << "No Solution";
	}else{
		a[q.front()] = w;
		for(int i = 1;i <= (1 << k);i ++){
			if(i != 1){
				cout << " ";
			}
			cout << a[i];
		}
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t = 1;
//	cin >> t;
	
	while(t --){
		solve();
	}
}

L2-048 寻宝图

给定一幅地图,其中有水域,有陆地。被水域完全环绕的陆地是岛屿。有些岛屿上埋藏有宝藏,这些有宝藏的点也被标记出来了。本题就请你统计一下,给定的地图上一共有多少岛屿,其中有多少是有宝藏的岛屿。

解题思路

用bfs计算一下有多少个不为 0 0 0 的连通块就是岛屿的数量,然后再判断一下这个连通块中是否存在不等于 1 1 1 的点的就是宝藏的数量。

参考代码

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using ld = long double;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1};
void solve(){
	int n,m;
	cin >> n >> m;
	vector<string> s(n + 1);
	for(int i = 1;i <= n;i ++){
		cin >> s[i];
		s[i] = '?' + s[i];
	}
	vector<vector<int>> vis(n + 1,vector<int>(m + 1));
	int ans1 = 0,ans2 = 0;
	for(int i = 1;i <= n;i ++){
		for(int j = 1;j <= m;j ++){
			if(s[i][j] != '0' && !vis[i][j]){
				ans1 ++;
				queue<pair<int,int>> q;
				q.push({i,j});
				int f = 0;
				vis[i][j] = 1;
				while(!q.empty()){
					auto [x,y] = q.front();
					q.pop();
					if(s[x][y] != '1'){
						f = 1;
					}
					for(int k = 0;k < 4;k ++){
						int kx = dx[k] + x,ky = dy[k] + y;
						if(kx >= 1 && kx <= n && ky >= 1 && ky <= m && !vis[kx][ky]){
							if(s[kx][ky] != '0'){

								vis[kx][ky] = 1;
								q.push({kx,ky});
							}
						}
					}
				}
				if(f){
					ans2 ++;
				}
			}
		}
	}
	cout << ans1 << " " << ans2 << "\n";
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t = 1;
//	cin >> t;
	
	while(t --){
		solve();
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈童学哦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值