SMU 2026 Spring 天梯赛3题解

7-5 出生年

题意:

给定出生年份y和n,从y年开始往后找,找到第一个满足条件的年份:将其补零成4位后,不同数字的个数正好等于n。输出需要等待的年数x和找到的年份(4位输出)。

思路:

从y年开始枚举年份i,每次将i转为4位字符串(不足4位前面补0),用set统计不同数字个数,等于n时停止。输出i-y和该年份的4位字符串。

代码

点击查看代码
int f(string s){
	set<char>st;
	for(auto x:s){
		st.insert(x);
	}
	return st.size();
}

void solve() {
	int y,n;
	cin>>y>>n;
	for(int i=y;i;i++){
		string s=to_string(i);
		while(s.size()<4)s='0'+s;

		if(f(s)==n){
			cout<<i-y<<' '<<s<<endl;
			return;
		}
	}
	
}

7-6 情人节

题意:

给定点赞的朋友名单,每行一个人名,输入以英文句点.结束。需要找出第2个和第14个点赞的人。如果两人都存在,输出“A and B are inviting you to dinner...”;如果只有第2个人存在,输出“A is the only one for you...”;如果连第2个人都没有,输出“Momo... No one is for you ...”。

思路:

直接模拟输入过程,用计数器记录当前是第几个人,遇到第2个和第14个时保存名字。输入结束后根据保存情况输出对应字符串。

代码

点击查看代码
void solve() {
	int p=1;
	string s;
	string s1="",s2="";
	while(cin>>s){
		if(s==".")break;
		if(p==2){
			s1=s;
		}else if(p==14){
			s2=s;
		}
		p++;
	}
	if(s1.size()&&s2.size()){
		cout<<s1<<" and "<<s2<<" are inviting you to dinner..."<<endl;
	}else if(s1.size()){
		cout<<s1<<" is the only one for you..."<<endl;
	}else{
		cout<<"Momo... No one is for you ..."<<endl;
	}
	
}

7-7 点赞

题意:

给定 N 个列表,每个列表包含 K 个数字(标签编号从 1 到 1000),统计所有数字中出现次数最多的数字。如果出现次数相同,输出编号最大的那个。

思路:

使用数组记录每个标签的出现次数。输入完成后,从 1000 到 1 遍历数组,维护出现次数最多的标签编号。由于从大到小遍历,当出现次数严格大于当前最大次数时更新,这样次数相同时不会更新,从而保留编号较大的。

代码

点击查看代码
int a[N];
void solve() {
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		int k;
		cin>>k;
		for(int j=0;j<k;j++){
			int x;
			cin>>x;
			a[x]++;
		}
	}
	int mi=1000;
	for(int i=1000;i>=1;i--){
		if(a[i]>a[mi]){
			mi=i;
		}
	}
	cout<<mi<<' '<<a[mi]<<endl;
}

7-8 古风排版

题意:

给定一个正整数 N 和一个字符串,将字符串按古风排版输出。古风排版规则:将字符串按列排列,每列 N 个字符(最后一列可能不足 N 个),然后从右向左逐行输出。

思路:

计算需要的列数 M=(len-1)/N+1。构造一个 N 行 M 列的网格,按列填充字符串(从上到下,从左到右)。填充时,若字符不足则用空格补全。最后按行输出,每行从最后一列到第一列输出字符。

代码

点击查看代码
char a[1005][1005];
void solve() {
	int n;
	cin>>n;
	string s;
	getline(cin,s);
	getline(cin,s);
	for(int i=0;i<s.length();i++){
		a[i%n][i/n]=s[i];
	}
	int m=((int)s.length()-1)/n;
	for(int i=0;i<n;i++){
		for(int j=m;j>=0;j--){
			if(a[i][j]){
				cout<<a[i][j];
			}else{
				cout<<' ';
			}
		}cout<<endl;
	}
	
}


7-9 人以群分

题意:

给定 N 个人的活跃度,需要将他们分为外向型(活跃度高)和内向型(活跃度低)两组。要求两组人数尽可能接近,且两组总活跃度之差尽可能大。输出外向型人数、内向型人数和总活跃度之差的绝对值。

思路:

为使人数量接近,当 N 为偶数时,每组各分 N/2 人;当 N 为奇数时,外向组多一人(即 ceil(N/2)),内向组为 floor(N/2)。为最大化活跃度差,将活跃度最高的人分到外向组,最低的分到内向组。因此排序后,内向组取前 floor(N/2) 个人的活跃度和,外向组为剩余部分,计算差值即可。

代码

点击查看代码
int a[N];
void solve() {
	int n;
	cin >> n;
	int s = 0;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		s += a[i];
	}
	sort(a + 1, a + 1 + n);
	int s1 = 0;
	for (int i = 1; i <= n / 2; i++) {
		s1 += a[i];
	}
	int mx = s - s1 - s1;
	cout<<"Outgoing #: "<<n-n/2<<endl;
	cout<<"Introverted #: "<<n/2<<endl;
	cout<<"Diff = "<<mx<<endl;

}

7-10 多项式A除以B

题意

给定两个多项式A和B,按照多项式除法的定义计算商Q和余数R(R的次数小于B的次数)。输入按次数降序给出多项式的非零项,输出也要按次数降序输出非零项,系数保留1位小数,舍入后为0.0的项不输出。零多项式输出"0 0 0.0"。

思路

模拟多项式长除法:

  1. 用数组a存储A的系数,b存储B的系数,下标为次数
  2. 当A的最高次数ma >= B的最高次数mb时循环,
    计算当前商项的次数t = ma - mb,系数x = a[ma] / b[mb]
    将商项存入c[t]
    从A中减去B * x(注意次数偏移)
  3. 输出时遍历商和余数数组,保留1位小数,绝对值≥0.05才输出
  4. 特别注意零多项式的输出格式

代码

点击查看代码
double a[N],b[N];
double c[N],d[N];
void solve() {
	int n;
	cin >> n;
	int ma=0;
	for(int i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;
		a[x]+=y;
		ma=max(ma,x);
	}
	int m;
	cin >> m;
	int mb=0;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		b[x]+=y;
		mb=max(mb,x);
	}
//	cout<<ma<<' '<<mb<<endl;
	for(int i=ma;i>=mb;i--){
		double x=a[i]/b[mb];
		c[i-mb]+=x;
		for(int j=0;j<=mb;j++){
			a[i-mb+j]-=b[j]*x;
		}
//		for(int j=ma;j>=0;j--){
//			cout<<a[j]<<"x^"<<j<<' ';
//		}cout<<endl;
	}
	int s1=0;
	for(int i=ma-mb;i>=0;i--){
		s1+=(fabsl(c[i])>=0.05L);
	}
	int s2=0;
	for(int i=mb;i>=0;i--){
		s2+=(fabsl(a[i])>=0.05L);
	}
	cout<<s1<<' ';
	int ff=1;
	for(int i=ma-mb;i>=0;i--){
		if(fabsl(c[i])>=0.05L){
			if(ff){
				ff=0;
			}else{
				cout<<' ';
			}
			cout<<fixed<<setprecision(1)<<i<<" "<<c[i];
		}
	}
	if(s1==0)cout<<"0 0.0";

	cout<<endl;
	
	cout<<s2<<' ';
	ff=1;
	for(int i=mb;i>=0;i--){
		if(fabsl(a[i])>=0.05L){
			if(ff){
				ff=0;
			}else{
				cout<<' ';
			}
			cout<<fixed<<setprecision(1)<<i<<" "<<a[i];
		}
	}
	if(s2==0)cout<<"0 0.0";
	cout<<endl;
}

7-11 悄悄关注

题意:

给定一个用户的关注列表和点赞记录。找出点赞次数超过平均点赞次数且不在关注列表中的用户,按字典序升序输出。如果没有这样的用户,输出"Bing Mei You"。

思路:

用set存储关注列表的用户ID,用于快速查找。
读取点赞记录,计算总点赞数。
遍历点赞记录,筛选出点赞数超过平均值且不在关注列表中的用户。
将筛选出的用户按字典序排序后输出。

代码

点击查看代码
set<string>st;
string a[N];
int b[N];
void solve() {
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		string ss;
		cin>>ss;
		st.insert(ss);
	}
	int m;
	cin>>m;
	int sum=0;
	for(int i=1;i<=m;i++){
		cin>>a[i]>>b[i];
		sum+=b[i];
		
	}
	vector<string>v;
	for(int i=1;i<=m;i++){
		if(b[i]*m>sum&&st.count(a[i])==0){
			v.push_back(a[i]);
		}
	}
	sort(v.begin(),v.end());
	if(v.size()){
		for(auto x:v){
			cout<<x<<endl;
		}
	}else{
		cout<<"Bing Mei You\n";
	}
}

7-12 功夫传人

题意:

给定一个师门谱系,祖师爷功力为Z,每传一代减弱r%。得道者会将功力放大N倍。求所有得道者的功力总值的整数部分。

思路:

通过DFS计算每个节点的当前衰减次数,对于得道者(即没有徒弟的节点),根据其衰减次数和放大倍数计算功力并累加。

代码

点击查看代码
int a[N];
int b[N];
vector<int>g[N];
double z,r;
double ans=0;
void dfs(int x){
	
	if(g[x].size()==0){
		ans+=z*pow(1-0.01*r,a[x])*b[x];
	}
	for(auto u:g[x]){
		a[u]=a[x]+1;
		dfs(u);
	}
}
void solve() {
	int n;
	cin>>n;
	cin>>z>>r;
	for(int i=0;i<n;i++){
		int x;
		cin>>x;
		for(int j=0;j<x;j++){
			int y;
			cin>>y;
			g[i].push_back(y);
		}
		b[i]=1;
		if(x==0){
			cin>>b[i];
		}
	}
	a[0]=0;
	dfs(0);

	cout<<(int)ans<<endl;
}

7-13 非常弹的球

题意:

给定小球质量放大100倍的值 \(w\)(实际质量 \(m = w/100 \text{ kg}\))和每次落地动能损失百分比 \(p\)。小球从 \((0,0)\) 以角度 \(\text{ang}\)\(0 < \text{ang} < \pi/2\))抛出,初动能 \(1000 \text{ J}\)。每次落地后动能保留 \((1 - p\%)\) 并以相同角度继续弹跳。求小球能达到的最远水平距离(保留三位小数)。

思路:

为使总距离最大,应选择 \(\text{ang} = 45^\circ\),此时 \(\sin(2\theta) = 1\)。每次弹跳水平距离构成等比数列,公比 \(\alpha = 1 - p/100\)。总距离为:

\[S = \frac{v_0^2 \sin(2\theta)}{g} \sum_{i=0}^{\infty} \alpha^i = \frac{v_0^2}{g \cdot (p/100)} \]

其中 \(v_0^2 = \frac{2E_0}{m} = \frac{200000}{w}\)\(g = 9.8\),代入得:

\[S = \frac{20000000}{w \cdot g \cdot p} \]

直接计算该表达式即可。

代码

点击查看代码
void solve() {
	int w,p;
	cin>>w>>p;
	cout<<fixed<<setprecision(3)<<2*10000000ll/(9.8*w)/(p)<<endl;
	
}

7-14 周游世界

题意:

给定N个公司的线路(每个线路有多个站点),站点之间可双向通行。需要为K个查询规划从起点到终点的路线。要求路线满足:

  1. 总经停站最少(边数最少)
  2. 若边数相同,则换乘次数最少
    输出最短路径的边数,并按格式输出路线(只输出起点、换乘点和终点)

思路:

使用优先队列BFS,状态包含已走边数、换乘次数、当前站点、上一条边的公司。
比较时先比较边数,再比较换乘次数。
记录每个状态的前驱节点和公司用于路径回溯。
回溯时合并相同公司的连续路径段输出。

代码

点击查看代码
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
//#define double long double
#define PII pair<int,int>
#define PIV pair<int,vector<int>>
#define PIII pair<PII,PII>
#define LL __int128
const int N = 2e5 + 10, INF = 1e18;

int a[N];
int ls1[N];
int ls2[N];
int b[N];

vector<PII>g[N];

void solve() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int k;
		cin >> k;
		int ls = -1;
		for (int j = 1; j <= k; j++) {
			int x;
			cin >> x;
			if (j > 1) {
				g[x].push_back({ls, i});
				g[ls].push_back({x, i});
			}
			ls = x;
		}
	}
	int m;
	cin >> m;
	priority_queue<PIII, vector<PIII>, greater<PIII>>q;
	for (int l = 0; l < m; l++) {
		for (int j = 0; j <= 9999; j++) {
			a[j] = b[j] = 1e9;

		}
		int s, t;
		cin >> s >> t;
		q.push({{0, 0}, {s, 0}});
		a[s] = b[s] = 0;
		while (q.size()) {
			auto [u, v] = q.top();
			q.pop();
			auto [s1, s2] = u;
			auto [x, i] = v;
			for (auto [y, j] : g[x]) {
				int ss1 = 0, ss2 = 0;
				if (i == 0) {
					ss2 = s2;
				} else {
					ss2 = s2 + (i != j);
				}
				ss1 = s1 + 1;
				if (ss1 < a[y] || (ss1 == a[y] && ss2 < b[y])) {
					a[y] = ss1;
					b[y] = ss2;
					ls1[y] = x;
					ls2[y] = j;
					q.push({{ss1, ss2}, {y, j}});
				}
			}

		}

		if (a[t] != 1e9) {
			cout << a[t] << endl;
			vector<PII>q;
			int x = t;
			q.push_back({x, ls2[x]});
			while (x != s) {
				int xx, ii;
				xx = ls1[x];
				x = xx;
				q.push_back({x, ls2[x]});
			}
			reverse(q.begin(), q.end());
//			for(auto [x,y]:q){
//				cout<<x<<'-'<<y<<' ';
//			}cout<<endl;
			int ls = q[1].second;
			int lx = q[0].first;
			for (int i = 1; i < q.size(); i++) {
				if (q[i].second != ls) {
					cout << "Go by the line of company #" << ls << " from " << f(lx) << " to " << f(q[i - 1].first) << "." << endl;
					ls = q[i].second;
					lx = q[i - 1].first;
				}
			}
			cout << "Go by the line of company #" << ls << " from " << f(lx) << " to " << f(q.back().first) << "." << endl;
		} else {
			cout << "Sorry, no line is available.\n";
		}
	}

}

7-15 球队“食物链”

题意:

给定n支球队和它们之间的比赛结果矩阵。矩阵中W表示i战胜j,L表示i负于j,D表示平局。要求找到一个包含所有球队的排列,使得每个队都战胜过排列中它的下一个队,且最后一个队战胜过第一个队。如果存在多个这样的排列,输出字典序最小的;如果不存在,输出"No Solution"。

思路:

这是一个哈密顿回路问题,n最大20,可以用DFS+剪枝解决。为求字典序最小,从1开始DFS,并按从小到大顺序尝试下一个球队。剪枝:如果当前未访问的球队中没有一个能战胜起点球队1,那么不可能形成回路,直接剪枝。

代码

点击查看代码
int a[22][22];
int ans[22];
int res[22];
int vis[22];
int n;
int fl=0;
void dfs(int x,int s){
	if(fl)return;
	if(s==n){
		if(a[x][1]){
			fl=1;
			for(int i=1;i<=n;i++){
				ans[i]=res[i];
			}
		}
		return;
	}
	int ff=0;
	for(int i=1;i<=n;i++){
		if(!vis[i]&&a[i][1]){
			ff=1;
		}
	}
	if(!ff)return;
	for(int i=1;i<=n;i++){
		if(!vis[i]&&a[x][i]){
			vis[i]=1;
			res[s+1]=i;
			dfs(i,s+1);
			res[s+1]=0;
			vis[i]=0;
		}
	}
}

void solve() {
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			char x;
			cin>>x;
			if(x=='W'){
				a[i][j]=1;
			}else if(x=='L'){
				a[j][i]=1;
			}
		}
	}
	res[1]=1;
	vis[1]=1;
	dfs(1,1);
	if(fl){
		for(int i=1;i<=n;i++){
			cout<<ans[i]<<" \n"[i==n];
		}
	}else cout<<"No Solution\n";
}
posted @ 2026-03-10 15:59  ptlks  阅读(239)  评论(0)    收藏  举报