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"。
思路
模拟多项式长除法:
- 用数组a存储A的系数,b存储B的系数,下标为次数
- 当A的最高次数ma >= B的最高次数mb时循环,
计算当前商项的次数t = ma - mb,系数x = a[ma] / b[mb]
将商项存入c[t]
从A中减去B * x(注意次数偏移) - 输出时遍历商和余数数组,保留1位小数,绝对值≥0.05才输出
- 特别注意零多项式的输出格式
代码
点击查看代码
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\)。总距离为:
其中 \(v_0^2 = \frac{2E_0}{m} = \frac{200000}{w}\),\(g = 9.8\),代入得:
直接计算该表达式即可。
代码
点击查看代码
void solve() {
int w,p;
cin>>w>>p;
cout<<fixed<<setprecision(3)<<2*10000000ll/(9.8*w)/(p)<<endl;
}
7-14 周游世界
题意:
给定N个公司的线路(每个线路有多个站点),站点之间可双向通行。需要为K个查询规划从起点到终点的路线。要求路线满足:
- 总经停站最少(边数最少)
- 若边数相同,则换乘次数最少
输出最短路径的边数,并按格式输出路线(只输出起点、换乘点和终点)
思路:
使用优先队列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";
}

浙公网安备 33010602011771号