# 天梯赛题解

2022天梯赛题解

​ 其实还是自己的问题,太久没打这么正式的题了,算法能力也下降了太多,虽然在校赛后意识到了,然后开始看y总的课程,但是刷题还是不够,而且时间还太短;自己的读题能力也下降太多了,上次校赛是三个人所以读错题目也会有人纠正,但是这次就没人纠正了,所以在题意理解方面就出了很多偏差。令我感到懊恼的是我们2队就差好像二三十分就可以拿个团体国三,这样子有些学长学姐在保研上就可以加分了,可是我失误了,而且失误的实在是太严重了。

​ 唉,上学期的一些原因导致我不想熬夜,但是这学期的助理工作还有学业让我有没有办法在白天完成工作和学业后还能有多余的时间去学算法、做算法题,也只能调整策略了。下学期课程变多,还是不去当辅导员助理了。

​ 算了算了,意识到了也算是一件好事了,以后晚上回寝室后就看题和刷题吧,如果有cf比赛就和学长学姐一起打cf比赛,没有就学算法吧。

L1-8 静静的推荐

​ 本题末尾为最正确完美解法,前面是自己修正的过程;

​ 最开始的做法:也就是对于每个分数,我们建立二维数组再对其排序,但是这里的排序方式是有问题的,虽然绝大部分样例都能过。

​ 问题在于:对于例如当面时分线是90的时候,[189,1],[189,99]我这种做法就优先选择了[189,99]的人,而把[189,1]放在了第二批推荐名单里了,但实际上,我们可以主动选择[189,1]的人,然后让[189,99]作为题目中描述的“也可以接受”的人;

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5,N2=1e3+5;
struct node{
	int x,y;
}a[N];
int n,k,s;
bool cmp(node cc1,node cc2)
{
	if(cc1.x>cc2.x) return true;
	else if(cc1.x == cc2.x){
		return cc1.y > cc2.y;
	}
	return false;
}
int vis[N];//某个分数是否出现过 
int ex[N];//每个分数上有多少个因为某些要求不符合的人 
int main()
{
	int i,j;
	cin>>n>>k>>s;
	if(k <= 0){
		cout<<0<<"\n";
		return 0;
	}
	int cnt = 0;//低于175 
	for(i = 1;i <= n; i++){
		cin>>a[i].x>>a[i].y;
		if(a[i].x < 175) cnt++;
	}
	
	sort(a+1,a+1+n,cmp);
	
	for(i = 1;i <= n; i++){
		if(vis[a[i].x]){//出现过 
			if(a[i].y < s){//且没有过线 
				ex[a[i].x]++;
			}
		}
		vis[a[i].x] = 1;//标记 
	}
	int ans = n - cnt;
	k--;//本身的那一批 
	for(i = 175; i<=290; i++){
		if(k < ex[i]){ 
			ans -= (ex[i]-k);
		}
		//if(ex[i]) cout<<i<<" "<<ex[i]<<"\n";
	}
	ans = max(ans,0);
	cout<<ans;
}

正解:

#include<bits/stdc++.h>
using namespace std;

const int N =1e6+5;
vector<int> a[N];
set<int>start;//
int ex[N];
int main()
{
	int i,j;
	int n,k,s;cin>>n>>k>>s;
	
	for(i = 1;i <= n; i++){
		int x,y;cin>>x>>y;
		if(x >= 175){
			a[x].push_back(y);
			start.insert(x);
		}
	}
	
	for(auto it =start.begin();it != start.end(); it++){
		sort(a[*it].begin(),a[*it].end());
	}
	k--;
	int ans = 0;
	for(auto it =start.begin();it != start.end(); it++){
		if(!a[*it].empty()){
			ans++;
			a[*it].erase(a[*it].begin());
			if(!a[*it].empty()){
				for(j = a[*it].size()-1;j >= 0; j--){
					if(a[*it][j] < s && ex[*it] < k){
						ans++;
						ex[*it]++;
					}
					else if(ex[*it]<k){
						ans++;
					}
					a[*it].pop_back();
				}
			}
		}
	}
	
	cout<<ans;
	return 0;
}

​ 又去看了看别人的题解,我发现特么的根本就没有那么麻烦啊!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

​ 为什么考试的时候我考虑那么多,还没做出来,我是傻逼。

#include<bits/stdc++.h>
using namespace std;
int n,k,s;
int a[300];
int main()
{
	int i,j;
	cin>>n>>k>>s;
	int ans = 0;
	for(i = 1;i <= n; i++){
		int x,y;
		cin>>x>>y;
		if(x >= 175){
			if(y >= s) ans++;
			else a[x]++;
		}
	}
	for(i = 175;i <= 290; i++){
		ans+=min(k,a[i]);
	}
	cout<<ans<<"\n";
}

L2-1 插松枝

​ 又臭又长,搞人心态的一道题啊,就是考察栈和队列的使用,但是这个体面写得太nm复杂了,我还傻乎乎的去看它,调了半天还没做出来,其实自己读题能力太弱了。

​ 然后补题写了半天也还是有问题;

L2-2 老板的作息表

​ 可能是因为之前准备蓝桥杯的时候在日期问题上经常用三维数组来模拟年月日,所以看到这个题就想用三维数组来模拟时分秒,把对应给出放入时间段置为1,但是写着写着发现这种写法太麻烦了,写了快一百行了。

​ 但是实际上就是一个区间合并的问题。

我们用一个六位数来表示时间点(包括前导0),前两位表示时,中间两位表示分钟,后两位表示秒。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+6;
struct node{
	int s,e;//
}ex[N];
bool cmp(node x,node y)
{//优先按照结束时间递增排序,其次按照开始时间递增排序
	if(x.e < y.e) return true;
	else if(x.e == y.e){
		return x.s<y.s;
	}else{
		return false;
	}
}
void check(int &start)//用于检测数据是否合法,并修改不合法的数据;
{
	if(start%100>=60){
		start-=60;
		start+=100; 
	}
	if(start%10000/100>=60){
		start-=6000;
		start+=10000;
	}
}
int main()
{
	int i,j;
	int n;scanf("%d",&n);
	for(i = 1;i <= n; i++)
	{
		int a,b,c,x,y,z;
		scanf("%d:%d:%d - %d:%d:%d",&a,&b,&c,&x,&y,&z);
		int cc1 = a*10000+b*100+c;
		int cc2 = x*10000+y*100+z;
		ex[i].s = cc1,ex[i].e = cc2;// = {cc1,cc2};
	}
	sort(ex+1,ex+1+n,cmp);
	int start = 0;
	int f = 1;
	for(i = 1;i <= n; i++){
		if(ex[i].s>start){
			int end = ex[i].s;
			check(end);
			if(f == 0) printf("\n");
			printf("%02d:%02d:%02d - %02d:%02d:%02d",start/10000,start%10000/100,
			start%100,end/10000,end%10000/100,end%100);
			start = ex[i].e;
			check(start);
			f = 0;
		}else{
			start = ex[i].e;
			check(start);
		}
	}
	if(start != 235959){
		if(f == 0) printf("\n");
		printf("%02d:%02d:%02d - 23:59:59",start/10000,start%10000/100,
			start%100);
	}
}

​ 看了别人的解法,发现自己写的还是还是太麻烦了;

#include<bits/stdc++.h>
#define PSS pair<string,string> 
using namespace std;

int main()
{
	int i,j;
	int n;scanf("%d",&n);
	vector<PSS >q;
	for(i = 1;i <= n; i++){
		string a,b,c;
		cin>>a>>b>>c;
		q.push_back({a,c});
	}
	q.push_back({"","00:00:00"});
	q.push_back({"23:59:59",""});
	sort(q.begin(),q.end());//pair 默认对first升序,当first相同时对second升序; 
	
	int len = q.size();
	for(i = 0;i < len-1; i++){
		if(q[i].second != q[i+1].first){//因为题目说没有区间重叠的情况,所以不用写"<"
			cout<<q[i].second<<" - "<<q[i+1].first<<"\n";
		} 
	}
	return 0;	
}

L2-3 龙龙送外卖

​ 我还以是迪杰斯特拉,不过不是,好像还加入了树,然后dfs树,具体算法知识还有待补充;

L2-4 大众情人

​ 考察的知识点是弗洛伊德算法;

(8条消息) 弗洛伊德(Floyd)算法c++版_我叫大睿智的博客-CSDN博客_floyd算法c++

​ 因为题目中“从小蓝的眼中看去,他和小红之间的距离为 1,只差一层窗户纸;但在小红的眼里,她和小蓝之间的距离为 108000”,即说明a[x] [y] != a[y] [x],不具备传递性

#include<bits/stdc++.h>
using namespace std;
const int N = 505;

int a[N][N];
int dis[N];//每个人对应的最远的那个
int sex[N];//性别,0女性,1男性 
int main()
{
	int i,j,k;
	int n;cin>>n;
	for(i = 1;i <= n; i++){
		for(j = 1;j <= n; j++){
			a[i][j] = (i==j)?0:1e9;
		}
	}
	for(i = 1;i <= n; i++){
		char op[2];
		cin>>op>>k;
		if(*op=='M') sex[i] = 1;
		for(j = 1;j <= k; j++){
			int x,y;
			scanf("%d:%d",&x,&y);
			a[i][x] = y;// 
			//这里可以不用s[x][i] = s[i][x];,不具备传递性
		}
	} 
	//弗洛伊德 
	for(k = 1;k <= n; k++){//中转站 
		for(i = 1;i <= n; i++){
			for(j = 1;j <= n; j++){
				//不需要判断是否自己到自己,因为肯定是0
				a[i][j] = min(a[i][j],a[i][k]+a[k][j]);	 
			}
		}
	}
	
	for(i = 1;i <= n; i++){
		dis[i] = 0;
		for(j = 1;j <= n; j++){
			if(sex[i] != sex[j]){
				dis[i] = max(dis[i],a[j][i]);
				//因为异性缘是对i最无感的那个人决定的,所以是j->i,不是i->j; 
			}
		}
	}
	
	for(k = 0;k <= 1; k++){//先女性,后男性 
		int d_min = 2e9;
		for(i = 1;i <= n; i++){
			if(sex[i] == k){
				d_min = min(d_min,dis[i]);
			}
		}
		int f = 0;//第一个?
		for(j = 1;j <= n; j++){
			if(sex[j]==k&&dis[j] == d_min){
				if(f != 0) cout<<' ';
				cout<<j;
				f++;
			}
		}puts("");
	}
	
	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值