2022天梯赛L2题解笔记

目录

一、插松枝

二、老板的作息表

 方法一:

方法二、

三、龙龙送外卖

四、大众情人


一、插松枝

人造松枝加工场的工人需要将各种尺寸的塑料松针插到松枝干上,做成大大小小的松枝。他们的工作流程(并不)是这样的:

  • 每人手边有一只小盒子,初始状态为空。

  • 每人面前有用不完的松枝干和一个推送器,每次推送一片随机型号的松针片。

  • 工人首先捡起一根空的松枝干,从小盒子里摸出最上面的一片松针 —— 如果小盒子是空的,就从推送器上取一片松针。将这片松针插到枝干的最下面。

  • 工人在插后面的松针时,需要保证,每一步插到一根非空松枝干上的松针片,不能比前一步插上的松针片大。如果小盒子中最上面的松针满足要求,就取之插好;否则去推送器上取一片。如果推送器上拿到的仍然不满足要求,就把拿到的这片堆放到小盒子里,继续去推送器上取下一片。注意这里假设小盒子里的松针片是按放入的顺序堆叠起来的,工人每次只能取出最上面(即最后放入)的一片。

  • 当下列三种情况之一发生时,工人会结束手里的松枝制作,开始做下一个:

(1)小盒子已经满了,但推送器上取到的松针仍然不满足要求。此时将手中的松枝放到成品篮里,推送器上取到的松针压回推送器,开始下一根松枝的制作。

(2)小盒子中最上面的松针不满足要求,但推送器上已经没有松针了。此时将手中的松枝放到成品篮里,开始下一根松枝的制作。

(3)手中的松枝干上已经插满了松针,将之放到成品篮里,开始下一根松枝的制作。

现在给定推送器上顺序传过来的

N片松针的大小,以及小盒子和松枝的容量,请你编写程序自动列出每根成品松枝的信息。

输入格式:

输入在第一行中给出 3 个正整数:N(≤103),为推送器上松针片的数量;M(≤20)为小盒子能存放的松针片的最大数量;K(≤5)为一根松枝干上能插的松针片的最大数量。随后一行给出N个不超过100的正整数,为推送器上顺序推出的松针片的大小。

输出格式:

每支松枝成品的信息占一行,顺序给出自底向上每片松针的大小。数字间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

8 3 4

20 25 15 18 20 18 8 5

输出样例:

20 15

20 18 18 8

25 5

(模拟题 qwq

#include<iostream>
#include<queue>
#include<stack>
using namespace std;
int n,m,k;
queue<int> q;
stack<int> stk;
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m>>k;
	for(int i=0,x;i<n;i++){
		cin>>x;
		q.push(x);
	}
	while(stk.size()||q.size()){
		int f=1,last=200,ct=0;
		while(ct<k){
			if(stk.size()&&stk.top()<=last){//“与”运算若第一个条件为0则后面的都不会看,所以安全
				if(f)cout<<stk.top(),f=0;
				else cout<<' '<<stk.top();
				last=stk.top();
				stk.pop();
				ct++;
			}
			else if(q.size()){
				int t=q.front();
				if(t<=last){
					if(f)cout<<t,f=0;
					else cout<<' '<<t;
					last=t,q.pop();
					ct++;
				}
				else if(stk.size()<m){
						stk.push(t);
						q.pop();
				}
				else break;	
			}
			else break;
		}
		cout<<'\n';
	}
	return 0;
}

二、老板的作息表

新浪微博上有人发了某老板的作息时间表,表示其每天 4:30 就起床了。但立刻有眼尖的网友问:这时间表不完整啊,早上九点到下午一点干啥了?

本题就请你编写程序,检查任意一张时间表,找出其中没写出来的时间段。

输入格式:

输入第一行给出一个正整数N,为作息表上列出的时间段的个数。随后N行,每行给出一个时间段,格式为:

hh:mm:ss - hh:mm:ss

其中 hh、mm、ss 分别是两位数表示的小时、分钟、秒。第一个时间是开始时间,第二个是结束时间。题目保证所有时间都在一天之内(即从 00:00:00 到 23:59:59);每个区间间隔至少 1 秒;并且任意两个给出的时间区间最多只在一个端点有重合,没有区间重叠的情况。

 输出格式:

按照时间顺序列出时间表中没有出现的区间,每个区间占一行,格式与输入相同。题目保证至少存在一个区间需要输出。

 方法一

时间轴来标记有无记录,最后遍历输出,考虑到一天只有24个小时,可全部化作秒来记录,注意边界问题:l~r时间里,标记时是“左闭右开”的区间,(时间数量是r-l,不然就多了一个,变成了r-l+1了)【plus:介于pta格式总是奇奇怪怪,这里用sscanfsprintf方便读入和输出~】

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int M=24*60*60-1;
int n,line[M];
int Get(string x){
	int h,m,s;
	sscanf(x.c_str(),"%d:%d:%d",&h,&m,&s);
	return h*3600+m*60+s;
}
string Format(int x){
	int h,m,s; char str[10];
	h=x/3600;
	x%=3600;
	m=x/60;
	s=x%60;
	sprintf(str,"%02d:%02d:%02d",h,m,s);
	return str;
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin >>n;
	string a,b,c;
	while(n--){
		cin>>a>>c>>b;
		int l=Get(a), r=Get(b);
		while(l<r)line[l++]=1;//注意不能取到r,个数为r-l,不然就多了个1了 
	}
	for(int i=0;i<M;i++){
		if(!line[i]){
			int j=i+1;
			while(j<M&&!line[j])j++;
			cout<<Format(i)<<" - "<<Format(j)<<'\n';
			i=j;
		}
	}
	return 0;
}

方法二

排序,将每一个时间段用pair<string,string>放入set(自动排序无重复)中,用一个last变量记录上一次的终止时间,与下一个的起始时间相比看是否相同来输出,(注意开头与结尾的处理)

#include<iostream>
#include<set>
#include<string>
using namespace std;
int n;
set< pair<string,string> > s;
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	string a,b,c, last="-1";
	cin>>n;
	while(n--){
		cin>>a>>c>>b;
		s.insert({a,b});
	}
	for(auto &p:s){
		if(last=="-1"){
			last=p.second;
			if(p.first!="00:00:00")
				cout<<"00:00:00 - "<<p.first<<'\n';
		}
		else{
			if(last==p.first)last=p.second;
			else{
				cout<<last<<" - "<<p.first<<'\n';
				last=p.second; 
			}
		}
	}
	if(last!="23:59:59")cout<<last<<" - "<<"23:59:59"<<'\n';
	return 0;
}

三、龙龙送外卖

龙龙是“饱了呀”外卖软件的注册骑手,负责送帕特小区的外卖。帕特小区的构造非常特别,都是双向道路且没有构成环 —— 你可以简单地认为小区的路构成了一棵树,根结点是外卖站,树上的结点就是要送餐的地址。

每到中午 12 点,帕特小区就进入了点餐高峰。一开始,只有一两个地方点外卖,龙龙简单就送好了;但随着大数据的分析,龙龙被派了更多的单子,也就送得越来越累……

看着一大堆订单,龙龙想知道,从外卖站出发,访问所有点了外卖的地方至少一次(这样才能把外卖送到)所需的最短路程的距离到底是多少?每次新增一个点外卖的地址,他就想估算一遍整体工作量,这样他就可以搞明白新增一个地址给他带来了多少负担。

输入格式:

输入第一行是两个数N和M(2≤N≤1e5,1≤M≤1e5),分别对应树上节点的个数(包括外卖站),以及新增的送餐地址的个数。

接下来首先是一行N个数,第i个数表示第i个点的双亲节点的编号。节点编号从 1 到N,外卖站的双亲编号定义为−1。

接下来有M行,每行给出一个新增的送餐地点的编号 Xi。保证送餐地点中不会有外卖站,但地点有可能会重复。

为了方便计算,我们可以假设龙龙一开始一个地址的外卖都不用送,两个相邻的地点之间的路径长度统一设为 1,且从外卖站出发可以访问到所有地点。

注意:所有送餐地址可以按任意顺序访问,且完成送餐后无需返回外卖站

输出格式:

对于每个新增的地点,在一行内输出题目需要求的最短路程的距离。

输入样例:

7 4

-1 1 1 1 2 2 3

5

6

2

4

输出样例:

2

4

4

6

思路:如果回到起点的话,我们发现恰好要走的每条边都走了两次,那么要是不回到起点,就是最后一个外卖点到根结点的边只走了一次,所以只要让最后走的点是最深的(即离根最远),减掉的最多,总路程最短。

 

#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+5,M=1e5+5;
int p[N],d[N],n,m,sum,ma;
int dfs(int x){
	if(p[x]==-1||d[x]>0)return d[x];
	sum++;
	d[x]=dfs(p[x])+1; //记忆化搜索O(n) 
	return d[x];
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>p[i];
	while(m--){
		int x;
		cin>>x;
		ma=max(ma,dfs(x));
		cout<<sum*2-ma<<'\n';
	}
	return 0;
}

四、大众情人

人与人之间总有一点距离感。我们假定两个人之间的亲密程度跟他们之间的距离感成反比,并且距离感是单向的。例如小蓝对小红患了单相思,从小蓝的眼中看去,他和小红之间的距离为 1,只差一层窗户纸;但在小红的眼里,她和小蓝之间的距离为 108000,差了十万八千里…… 另外,我们进一步假定,距离感在认识的人之间是可传递的。例如小绿觉得自己跟小蓝之间的距离为 2,则即使小绿并不直接认识小红,我们也默认小绿早晚会认识小红,并且因为跟小蓝很亲近的关系,小绿会觉得自己跟小红之间的距离为 1+2=3。当然这带来一个问题,如果小绿本来也认识小红,或者他通过其他人也能认识小红,但通过不同渠道推导出来的距离感不一样,该怎么算呢?我们在这里做个简单定义,就将小绿对小红的距离感定义为所有推导出来的距离感的最小值。

一个人的异性缘不是由最喜欢他/她的那个异性决定的,而是由对他/她最无感的那个异性决定的。我们记一个人i在一个异性j眼中的距离感为 D_{ij};将i的“异性缘”定义为 1/max_{j\epsilon S(i)}\left \{ D_{ij} \right \},其中S(i)是相对于i的所有异性的集合。那么“大众情人”就是异性缘最好(值最大)的那个人。

本题就请你从给定的一批人与人之间的距离感中分别找出两个性别中的“大众情人”。

输入格式

输入在第一行中给出一个正整数N(≤500),为总人数。于是我们默认所有人从 1 到N编号。

随后N行,第i行描述了编号为i的人与其他人的关系,格式为:

性别 K 朋友1:距离1 朋友2:距离2 …… 朋友K:距离K

其中 性别 是这个人的性别,F 表示女性,M 表示男性;K(<N的非负整数)为这个人直接认识朋友数;随后给出的是这 K 个朋友的编号、以及这个人对该朋友的距离感。距离感是不超过 1e6的正整数。

题目保证给出的关系中一定两种性别的人都有,不会出现重复给出的关系,并且每个人的朋友中都不包含自己。

输出格式

第一行给出自身为女性的“大众情人”的编号,第二行给出自身为男性的“大众情人”的编号。如果存在并列,则按编号递增的顺序输出所有。数字间以一个空格分隔,行首尾不得有多余空格。

 

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
const int N=505;
const int inf=0x3f3f3f3f;
int n,d[N][N],ma[N];
char sex[N],s[2];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			if(j!=i)d[i][j]=inf;
		}
	int num,id,dis;
	for(int i=1;i<=n;i++){
		scanf("%s%d",s,&num);//用%s防止多余空格 
		sex[i]=s[0];
		while(num--){
			scanf("%d:%d",&id,&dis);
			d[i][id]=dis;
		}
	}
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(sex[i]!=sex[j])ma[i]=max(ma[i],d[j][i]);//ji别人认为到你的距离 
	for(auto c: string("FM")){ 
		int mi=inf; 
		for(int i=1;i<=n;i++){
			if(sex[i]==c)
				mi=min(mi,ma[i]);
		}
		bool f=1;
		for(int i=1;i<=n;i++)
			if(sex[i]==c&&ma[i]==mi){
				if(f)f=0;
				else printf(" ");
				printf("%d",i);
			}
		puts("");
	} 
	return 0;
}

  • 11
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值