SMU 2026 Spring 天梯赛6题解

7-4 小孩子才做选择,大人全都要

题意:

给定两个整数 a 和 b,正数表示狗粮的克数,负数表示储蓄盒的容量(绝对值)。阿汪会选择狗粮最多的盒子(只考虑正数),铲屎官会两个盒子都要。铲屎官打开盒子后,如果遇到储蓄盒,会用另一只盒子里的狗粮来装满它(不够的话自己买)。输出阿汪选择时能吃到狗粮的克数 A 和铲屎官全都要后阿汪能吃到狗粮的克数 B,并判断阿汪是赚了(B > A)、亏了(B < A)还是躺平(A = B = 0)。

思路:

  • 阿汪选择时能吃到狗粮的克数 A:取两个数中最大的正数,如果没有正数则为 0。
  • 铲屎官全都要后阿汪能吃到狗粮的克数 B
    1. 两个都是正数:狗粮总和。
    2. 一正一负:用正数装满负数,剩余部分为正则给阿汪,否则为 0。
    3. 两个都是负数:阿汪得到 0。
  • 比较
    • 如果 A = B = 0,输出躺平脸。
    • 如果 B > A,阿汪赚了,输出笑脸。
    • 否则阿汪亏了,输出哭脸。

代码

点击查看代码
void solve() {
	int n,m;
	cin>>n>>m;
	if(n>0&&m>0){
		cout<<max(n,m)<<" "<<n+m<<endl;
		cout<<"^_^\n";
	}else if(n<0&&m<0){
		cout<<0<<" "<<0<<endl;
		cout<<"-_-\n";
	}else{
		cout<<max(n,m)<<' '<<max(0ll,n+m)<<endl;
		cout<<"T_T\n";
	}
}

7-5 吃火锅

题意:

读入多行字符串,直到遇到单独一行只有一个英文句点"."为止。统计总行数(不包括结束行),然后检查每一行是否包含子串"chi1 huo3 guo1"。输出总行数,然后如果至少有一行包含该子串,则输出第一次出现的行号(从1开始)以及包含该子串的总行数;否则输出"-_-#"。

思路:

直接遍历每一行,使用string的find方法查找子串,统计出现次数并记录第一次出现的位置。

代码

点击查看代码
void solve() {
	int n=0;
	string s;
	int mi=0,ss=0;
	while(getline(cin,s)){
		if(s==".")break;
		n++;
		if(s.find("chi1 huo3 guo1")!=-1){
			if(!mi)mi=n;
			ss++;
		}
	}
	cout<<n<<endl;
	if(mi)cout<<mi<<' '<<ss<<endl;
	else cout<<"-_-#\n";
}

7-6 外星人的一天

题意:

给定 N 个地球人的时刻,格式为 Day hh:mm,其中 Day 取值 0~6 对应周日至周六。需要转换为外星人的时刻。外星人的一天有 48 小时,一周只工作三天(对应地球的周一至周六),周日休息。转换规则:若地球人是周日(Day=0),则直接输出原时间;否则,将地球人时间除以 2(分钟向下取整),并将地球人的星期几映射到外星人的星期几(地球人周一和周二对应外星人周一,周三和周四对应外星人周二,周五和周六对应外星人周三)。

思路:

对于每个输入,若 Day=0 直接输出。否则,利用奇偶性合并地球人的两天为外星人的一天:若 Day 为偶数,则先将小时加上 24,使得该天的时间相当于从前一天开始累计。然后外星人 Day 计算为 (d+1)/2。接着计算总分钟数 m + h*60,除以 2 得到外星人总分钟数,再转换为小时和分钟输出即可。

代码

点击查看代码
void solve() {
	int d,h,m;
	char x;
	string hh,mm;
	cin>>d>>h>>x>>m;
	if(d==0){
		hh=to_string(h);
		mm=to_string(m);
		if(hh.size()<2)hh='0'+hh;
		if(mm.size()<2)mm='0'+mm;
		cout<<d<<' '<<hh<<':'<<mm<<endl;
		return;
	}
	if(d%2==0){
		h+=24;
	}
	d=(d+1)/2;
	m+=h*60;
	m/=2;
	h=m/60;
	m%=60;
	hh=to_string(h);
	mm=to_string(m);
	if(hh.size()<2)hh='0'+hh;
	if(mm.size()<2)mm='0'+mm;
	cout<<d<<' '<<hh<<":"<<mm<<endl;
}

7-7 随机输一次

题意:

给定 \(N\) 个正整数 \(K_i\),表示第i次输局之后应该隔 \(K_{i+1}\) 次再让下一个输局。然后循环输入对方的出招("ChuiZi"、"JianDao"、"Bu"),需要根据当前应该是赢还是输,输出对应的克制或输招。赢局输出克制对手的招,输局输出被对手克制的招。

思路:

用0、1、2分别表示锤子、剪刀、布,映射关系为:锤子克剪刀(0>1),剪刀克布(1>2),布克锤子(2>0)。赢局时输出对手招数+2再对3取模,输局时输出对手招数+1再对3取模。用计数器cnt记录当前连续赢的次数,当cnt等于当前K值时输一局并重置cnt,然后取下一个K值(循环使用)。

代码

点击查看代码
int a[N];
map<string,int>mp;
string ss[3]={"ChuiZi","JianDao","Bu"};
void solve() {
	mp["ChuiZi"]=0;
	mp["JianDao"]=1;
	mp["Bu"]=2;
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	int p=0,s=0;
	string t;
	while(cin>>t){
		if(t=="End")break;
		s++;
//		cout<<s<<' '<<a[p]<<endl;

		if(s==a[p]+1){
			
			cout<<ss[(mp[t]+1)%3]<<endl;
			s=0;
			p++;
			p%=n;
		}else{
			cout<<ss[(mp[t]+2)%3]<<endl;
		}
		
	}
}

7-8 谷歌的招聘

题意:

给定一个长度为 L 的数字字符串 N,找出最早出现的长度为 K 的子串,使得这个子串表示的整数是素数。如果不存在,输出 404。

思路:

遍历所有长度为 K 的子串,将每个子串转换为整数,判断是否为素数。素数判断采用试除法:预先用线性筛法求出所有小于等于 \(10^6\) 的素数,对于每个待判断的数,若小于等于 \(10^6\) 直接查表,否则用素数表中的素数试除。

代码

点击查看代码
bool flg[N];
int tot = 0;
void getMu(int n) {
	mu[1] = 1;
	flg[0]=flg[1]=1;
	for (int i = 2; i <= n; ++i) {
		if (!flg[i]) p[++tot] = i, mu[i] = -1;
		for (int j = 1; j <= tot && i * p[j] <= n; ++j) {
			flg[i * p[j]] = 1;
			if (i % p[j] == 0) {
				mu[i * p[j]] = 0;
				break;
			}
			mu[i * p[j]] = -mu[i];
		}
	}
//	for(int i=2;i<=n;i++){
//		mu[i]+=mu[i-1];
//	}
}

//int fac[N], inv[N];
int qpow(int x, int n) {
	int ans = 1;
	while (n) {
		if (n & 1) {
			ans *= x;
		}
		x *= x;
		n >>= 1;
	}
	return ans;
}

int a[N];
string s;

bool cmp(int x){
//	cout<<x<<' '<<flg[x]<<endl;

	if(x<=1e6)return !flg[x];
	int pp=1;
	while(x>1&&pp<=tot&&p[pp]*p[pp]<=x){
		if(x%p[pp]==0)return 0;
		pp++;
	}
	return 1;
}

int f(int l,int r){
	int x=0;
	for(int i=l;i<=r;i++){
		x*=10;
		x+=s[i]-'0';
		
	}
	return x;
	
}
void solve() {
	getMu(1e6);
	int L,K;
	cin>>L>>K;
	cin>>s;
	for(int i=0;i+K-1<L;i++){
		if(cmp(f(i,i+K-1))){
			for(int j=i;j<i+K;j++)cout<<s[j];
			return;
		}
	}
	cout<<404<<endl;
}

7-9 三足鼎立

题意:

给定本国实力值P和n个其他国家的实力值。求有多少种选择两个国家结盟的方式,使得三个国家(本国和选中的两个国家)满足“三足鼎立”:任意两国的实力之和大于第三国。

思路:

题意等价于,在n个数中选两个和P组成三角形的三边。
设选中的两个国家实力为a和b,考虑三角形的性质,条件等价于|a-b| < P 且 a+b > P。将数组排序后,对于每个a[i],需要找到j>i使得a[j] > P-a[i]且a[j] < a[i]+P。通过二分查找上下界,累加区间长度即可。

代码

点击查看代码
void solve() {
	int n;
	int P;
	int ans=0;
	cin>>n>>P;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++){
		if(a[i]>=P){
			int p1=upper_bound(a+1,a+1+n,a[i]-P)-a-1;
			int p2=lower_bound(a+1,a+1+n,a[i]+P)-a-1;
			p1=max(p1,i);
//			cout<<p1<<' '<<p2<<endl;
//			
//			cout<<a[p1+1]<<' '<<a[p2]<<endl;
			ans+=max(0ll,p2-p1);
		}else{
			int p1=upper_bound(a+1,a+1+n,P-a[i])-a-1;
			int p2=lower_bound(a+1,a+1+n,a[i]+P)-a-1;
			p1=max(p1,i);
//			cout<<p1<<' '<<p2<<endl;
//			cout<<a[p1+1]<<' '<<a[p2]<<endl;
			ans+=max(0ll,p2-p1);
		}
	}
	cout<<ans<<endl;
}

7-10 哈利·波特的考试

题意:

给定一个无向带权图,有 \(n\) 个节点(动物)和 \(m\) 条边(魔咒),每条边有长度(魔咒字符数)。要求找到一个节点 \(x\),使得 \(x\) 到所有其他节点的最短距离中的最大值最小化。即选择 \(x\) 使得 \(\max_{1 \le i \le n} dist(x,i)\) 最小,其中 \(dist(x,i)\)\(x\)\(i\) 的最短距离。如果存在多个满足条件的 \(x\),输出编号最小的。如果图不连通(存在节点不可达),则输出 0。

思路:

使用 Floyd 算法求出所有节点对之间的最短距离,复杂度 \(O(n^3)\)。然后对每个节点 \(i\),计算它到其他节点的最远距离 \(max_i\),即该节点变为最难变的动物所需的魔咒长度。取所有 \(max_i\) 中最小的对应的节点 \(i\),若有多个取编号最小。若存在节点 \(i\) 到某个节点不可达(距离为无穷大),则该 \(max_i\) 无效,如果所有节点都有不可达的情况,输出 0。

代码

点击查看代码
int mn[105][105];
void solve() {
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (i != j)mn[i][j] = 1e18;
			else mn[i][j] = 0;
		}
	}
	for (int i = 1; i <= m; i++) {
		int x, y, w;
		cin >> x >> y >> w;
		mn[x][y] = min(mn[x][y], w);
		mn[y][x] = mn[x][y];
	}
	for (int k = 1; k <= n; k++) {

		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				mn[i][j] = min(mn[i][j], mn[i][k] + mn[k][j]);
			}
		}
	}
	int mi = 0, mx = 1e18;
	for (int i = 1; i <= n; i++) {
		int ss = 0;
		for (int j = 1; j <= n; j++) {
			ss = max(ss, mn[i][j]);
		}
		if (ss < mx) {
			mx = ss;
			mi = i;
		}
	}
	if (mi)cout << mi << ' ' << mx << endl;
	else cout << 0 << endl;
}

7-11 城市间紧急救援

题意:

给定n个城市,每个城市有一定数量的救援队,以及连接城市的m条道路和长度。从起点s到终点d,求:

  1. 最短路径的数量
  2. 最短路径中能召集的最多救援队数量(包括起点终点)
  3. 输出具体的路径

思路:

Dijkstra算法的变形。在求最短路的同时,维护:

  • 最短路数量
  • 救援队最大值
  • 前驱节点用于输出路径

当找到更短的路径时,更新距离、救援队数量、路径数和前驱。
当路径长度相同时,如果救援队数量更大,更新救援队数量和前驱;同时累加路径数量。

代码

点击查看代码
int a[N];
int sum[N],vis[N];
int lst[N];
vector<PII>g[N];
PII mn[N];
void solve(){
	int n,m,s,d;
	cin>>n>>m>>s>>d;
	for(int i=0;i<n;i++){
		cin>>a[i];
		mn[i]={1e18,0};
	}
	for(int i=1;i<=m;i++){
		int x,y,w;
		cin>>x>>y>>w;
		g[x].push_back({y,w});
		g[y].push_back({x,w});
	}
	priority_queue<PIII,vector<PIII>,less<PIII>>q;
	q.push({{0,a[s]},s});
	mn[s]={0,a[s]};
	sum[s]=1;
	while(q.size()){
		auto [u,x]=q.top();
		q.pop();
		if(vis[x])continue;
		vis[x]=1;
		auto [s1,s2]=u;
		s1=-s1;
		for(auto [y,w]:g[x]){
			int ss1=s1+w;
			int ss2=s2+a[y];
			if(ss1<mn[y].first){
				mn[y]={ss1,ss2};
				sum[y]=sum[x];
				lst[y]=x;
				q.push({{-ss1,ss2},y});
			}else if(ss1==mn[y].first){
				sum[y]+=sum[x];
				if(ss2>mn[y].second){
					mn[y]={ss1,ss2};
					lst[y]=x;
					q.push({{-ss1,ss2},y});
				}
			}
		}
	}
	cout<<sum[d]<<" "<<mn[d].second<<endl;
	vector<int>ans;
	int x=d;
	while(x!=s){
		ans.push_back(x);
		x=lst[x];
	}
	ans.push_back(s);
	reverse(ans.begin(),ans.end());
	int ff=1;
	for(auto x:ans){
		if(ff){
			ff=0;
		}else{
			cout<<' ';
		}
		cout<<x;
	}
}

7-12 智能护理中心统计

题意:

有N个老人和若干个管理结点构成一棵树。管理结点用不超过4个大写字母表示,老人用1-N的编号表示。每个结点有唯一的上级,系统根结点没有上级。有三种操作:

  1. T x y:将编号为x的老人转移到管理结点y下
  2. Q y:查询管理结点y所负责的所有老人数量(包括其子树中的所有老人)
  3. E:结束程序

思路:

用并查集思想存储父子关系,每个结点的父节点为其直接上级。对于转移操作,需要将老人从其原父节点的孩子列表中删除,然后加入新父节点的孩子列表。对于查询操作,从该结点开始DFS遍历其子树,统计所有老人编号(≤N)的数量。

代码

点击查看代码
vector<int> g[N];
int fa[N];
unordered_map<string, int> mp;
int n, m, p;

void solve() {
	cin >> n >> m;
	p = n + 1;
	for (int i = 0; i < m; i++) {
		string a, b;
		cin >> a >> b;
		int x, y;
		if (a[0] >= '0' && a[0] <= '9') x = stoi(a);
		else {
			if (!mp.count(a)) mp[a] = p++;
			x = mp[a];
		}
		if (!mp.count(b)) mp[b] = p++;
		y = mp[b];
		fa[x] = y;
		g[y].push_back(x);
	}
	string s;
	while (cin >> s) {
		if (s == "T") {
			int x;
			string y;
			cin >> x >> y;
			int yy = mp[y];
			if (fa[x] == yy) continue;
			for (int i = 0; i < g[fa[x]].size(); i++) {
				if (g[fa[x]][i] == x) {
					swap(g[fa[x]][i], g[fa[x]].back());
					g[fa[x]].pop_back();
					break;
				}
			}
			g[yy].push_back(x);
			fa[x] = yy;
		} else if (s == "Q") {
			string x;
			cin >> x;
			int xx = mp[x];
			int cnt = 0;
			vector<int> st;
			st.push_back(xx);
			while (!st.empty()) {
				int u = st.back();
				st.pop_back();
				if (u <= n) cnt++;
				for (int v : g[u]) {
					st.push_back(v);
				}
			}
			cout << cnt << endl;
		} else  break;
	}
}

7-13 千手观音

题意:

给定 N 个字符串,每个字符串表示一个数字,数字由若干符号用 '.' 连接而成(类似人类十进制但这里是 10000 进制)。已知这些数字严格递增,需要推断出所有符号的相对大小顺序。如果顺序无法确定,则按字典序升序排列。

思路:

将每个符号看作图中的一个节点。通过比较相邻两个数字,找到第一个不同的符号,建立从前一个符号指向后一个符号的有向边(表示小于关系)。建图后使用拓扑排序,将入度为 0 的节点放入优先队列中按字典序排序,每次取出字典序最小的符号输出,以保证不确定时按字典序排列,再更新相邻点的入度,重复操作。

代码

点击查看代码
vector<string> f(string s){
	vector<string>v;
	string ss="";
	for(int i=0;i<s.length();i++){
		if(s[i]=='.'){
			v.push_back(ss);
			ss="";
			continue;
		}
		ss+=s[i];
		
	}
	v.push_back(ss);
	return v;
}
vector<int>g[N];

vector<string>ans;
string S[N];
int d[N];
unordered_map<string,int>str;


void solve() {
	int n;
	cin>>n;
	string s;
	getline(cin,s);
	vector<string> ls;
	int p=0;
	for(int i=1;i<=n;i++){
		getline(cin,s);
		vector<string>ss=f(s);
		for(auto sss:ss){
			if(str.count(sss)==0){
				str[sss]=++p;
				S[p]=sss;
			}
		}
		if(ss.size()==ls.size()){
			int p=0;
			while(p<ss.size()&&ss[p]==ls[p]){
				p++;
			}
			if(p<ss.size()){
				g[str[ls[p]]].push_back(str[ss[p]]);
				d[str[ss[p]]]++;
			}
		}
		
		ls=ss;
	}
	priority_queue<string,vector<string>,greater<string>>q;
	for(auto [x,y]:str){
		if(d[y]==0){
			q.push(x);
		}
	}
	while(q.size()){
		auto x=q.top();
		ans.push_back(x);
		q.pop();
		for(auto y:g[str[x]]){
			d[y]--;
			if(d[y]==0){
				q.push(S[y]);
			}
		}
		
	}
	int ff=1;
	for(auto x:ans){
		if(ff){
			ff=0;
		}else{
			cout<<'.';
		}
		cout<<x;
	}cout<<endl;

}

7-14 夺宝大赛

题意:

给定一个 n×m 的地图,0 表示障碍,1 表示可通过,2 表示大本营。有 k 支队伍从不同起点出发,每次可以向四个方向移动一格(耗时 1),不能走障碍。多支队伍同时到达大本营会发生火拼(这些队伍都失败)。求最先到达大本营且不发生火拼的队伍编号及其到达时间。

思路:

从大本营出发 BFS 计算每个可达点到终点的最短距离。然后对于每支队伍,记录其到达时间并按时间排序。找到唯一的最小时间(即前一个和后一个队伍的时间都不同)的队伍即为获胜者。
本题给的是 m 行 n 列,注意行列顺序。

代码

点击查看代码
int a[105][105];
int mn[105][105];
int vis[105][105];
PII b[N];
int xx[4]={0,1,0,-1};
int yy[4]={1,0,-1,0};
void solve() {
	int n,m;
	cin>>n>>m;
	int sx,sy;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
			mn[i][j]=1e18;
			if(a[i][j]==2){
				sx=i;
				sy=j;
			}
		}
	}
	int k;
	cin>>k;
	for(int i=1;i<=k;i++){
		int x,y;
		cin>>y>>x;
		b[i]={x,y};
	}
	priority_queue<PIII,vector<PIII>,greater<PIII>>q;
	q.push({0,{sx,sy}});
	mn[sx][sy]=0;
	while(q.size()){
		auto [s,u]=q.top();
		auto [x,y]=u;
		q.pop();
		if(vis[x][y]){
			continue;
		}
		for(int i=0;i<4;i++){
			int X=xx[i]+x;
			int Y=yy[i]+y;
			if(a[X][Y]&&s+1<mn[X][Y]){
				mn[X][Y]=s+1;
				q.push({s+1,{X,Y}});
			}
		}
	}
	vector<PII>ans;
	for(int i=1;i<=k;i++){
		ans.push_back({mn[b[i].first][b[i].second],i});
	}
	sort(ans.begin(),ans.end());
	for(int i=0;i<ans.size();i++){
		if(i&&ans[i].first==ans[i-1].first)continue;
		else if(i+1<ans.size()&&ans[i].first==ans[i+1].first)continue;
		else if(ans[i].first==1e18)continue;
		cout<<ans[i].second<<' '<<ans[i].first<<endl;
		return;
	}
	cout<<"No winner.\n";
}

7-15 喊山

题意:

给定n个山头(编号1-n),m条双向边表示可以互相听到,每个山头最多有2个相邻山头。有k个查询,每个查询给出起点s,要求输出从s出发最远能到达的山头编号。如果有多个最远山头,输出编号最小的。如果s孤立无法到达任何其他山头,输出0。

思路:

对每个查询起点s进行BFS,记录距离和当前最远距离的最小编号节点。使用队列进行BFS遍历,用数组记录距离,同时更新最远距离对应的最小编号节点。

代码

点击查看代码
vector<int>g[N];
int d[N],vis[N];

void solve() {
	int n,m,k;
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	for(int l=1;l<=k;l++){
		for(int i=1;i<=n;i++){
			vis[i]=0;
			d[i]=1e9;
		}
		int s;
		cin>>s;
		queue<int>q;
		q.push(s);
		d[s]=0;
		vis[s]=1;
		int mx=0,mi=0;
		while(q.size()){
			auto x=q.front();
//			cout<<x<<":";
			q.pop();
			
			for(auto y:g[x]){
				if(vis[y])continue;
//				cout<<y<<' ';
				d[y]=d[x]+1;
				if(d[y]>mx){
					mx=d[y];
					mi=y;
				}else if(d[y]==mx&&y<mi){
					mi=y;
				}
				vis[y]=1;
				q.push(y);
			}
//			cout<<endl;
		}
		cout<<mi<<endl;
	}
}
posted @ 2026-03-31 16:00  ptlks  阅读(232)  评论(0)    收藏  举报