2025 年河南工业大学天梯赛 题解

L1-1 哪吒的宣言

C 语言

#include <stdio.h>

int main(void) {
    printf("If the world won't accept me, I'll twist it to my will!");
    return 0;
}

Python

print("If the world won't accept me, I'll twist it to my will!")

L1-2 哪吒求和

#include<stdio.h>
int a[12];
int main()
{
	int sum=0;
	for(int i=1;i<=10;i++)
	{
		scanf("%d",&a[i]);
		sum+=a[i];
	}
	printf("%d",sum);
	return 0;
 }

L1-3 找到HarryPotter

读入一个字符串然后遍历一遍即可,找到字符串中为h的字符,然后将它替换

#include<iostream>

using namespace std;

signed main() {
    string s;
    cin >> s;
    for (int i = 0; i < s.size(); i++) {
        if (s[i] == 'h') {
            s[i] = '#';
        }
    }
    cout << s << endl;
    return 0;
}

L1-4 Shinji Ikari

只需要判断每个下标是否是质数即可。该题数据量较小,可暴力判断。

注意1不是质数。

#include <bits/stdc++.h>  
  
#define pii pair<int,int>  
#define piii pair<int, pair<int, int>>  
#define int long long  
  
using namespace std;  
  
bool isprime(int x) {  
    if (x == 1) return 0;  
    if (x == 2 || x == 3) return 1;  
    for (int i = 2; i <x; i++) {  //此处可直接暴力到x-1
        if (x % i == 0) {  
            return 0;  
        }  
    }  
    return 1;  
}  
  
void solve() {  
    int n;  
    cin >> n;  
    for (int i = 1; i <= n; i++) {  
        int x;  
        cin >> x;  
        if (!isprime(i)) cout << x << " ";  
    }  
}  
  
signed main() {  
  
  
    int T = 1;  
    while (T--) {  
        solve();  
    }  
    return 0;  
}

L1-5 零件检测

通读题干后可以得出题目要求的是n次循环中每个零件的实际误差之和除以n,与p进行比较,如果该平均值不大于p,输出Yes,否则输出No,并保留6位小数输出实际误差。 代码如下:

#include<stdio.h>

int main()
{
	int n ;
	double k , p ;
	
	scanf("%d%lf%lf",&n , &k , &p) ;
	
	double sum = 0 ;
	for(int i = 1 ; i <= n ; i ++) {
		double x ;
		scanf("%lf", &x) ;
		if(x - k >= 0)	sum += x - k ;
		else sum += k - x ;
		
	}
	
	sum = (1.00 * sum) / (1.00 * n) ;
	
	if(sum <= p)	printf("Yes\n") ;
	else printf("No %6lf\n" , sum) ;
	
	return 0 ;
}

L1-6 用电量预测误差

按题意来,无需多言。

C 语言

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int P[24], A[24];
    
    for (int i = 0; i < 24; ++i) {
        scanf("%d", &P[i]);
    }
    
    for (int i = 0; i < 24; ++i) {
        scanf("%d", &A[i]);
    }
    
    int maxDiff = 0;
    for (int i = 0; i < 24; ++i) {
        int diff = abs(P[i] - A[i]);
        if (diff > maxDiff) {
            maxDiff = diff;
        }
    }
    
    printf("%d\n", maxDiff);
    
    return 0;
}

Python

P = map(int, input().split())
A = map(int, input().split())
print(max(abs(p - a) for p, a in zip(P, A)))

L1-7 跨文化交际

遍历字符串,如果在原字符串S中遇到字符C,输出P字符串即可。请注意吸收换行符,代码如下:

#include<stdio.h>
#include<string.h>

const int N = 2e5 + 10 ;

int main()
{
	char s[N] ;
	scanf("%s",s) ;
	getchar() ;
	
	char c ;
	scanf("%c" , &c) ;
	getchar() ;
	
	char p[N] ;
	scanf("%s" , p) ; 
	
	for(int i = 0 ; i < strlen(s) ; i ++) {
		if(s[i] != c) {
			printf("%c" , s[i]) ;
		} else {
			printf("%c" , s[i]) ;
			for(int j = 0 ; j < strlen(p) ; j ++) {
				printf("%c" , p[j]) ;
			}
		}
	}
	
	return 0 ; 
}

L1-8 马拉松站点补给

通过循环模拟分配过程:

初始化长度为num_people的全零数组

定义当前应分配数量current,从1开始递增

循环分配能量胶,每次取min(current,剩余数量)

利用取模运算实现循环索引

#include <vector> 
#include <algorithm>
#include<iostream> 
int res[10000] = {0};
void distributeSupplies(int supplies, int num_stations) {
    int current = 1;  
   int index = 0;

    while (supplies > 0) {
        int allocation = std::min(current, supplies);      
        res[index % num_stations] += allocation;     
        supplies -= allocation; 
        current++; 
        index++;
    }
} int main() {
    int n, m;   
    std::cin >> n >> m;
    distributeSupplies(n, m);
    for (int i = 0; i < m; i++)
    {
        std::cout << res[i] << " ";
    }     return 0;
}

复杂度分析

时间复杂度:O(√n),因每次分配量递增,循环次数约为√(2*n)

空间复杂度:O(m),仅需维护结果数组

该方法通过循环和取模运算高效实现了符合赛事规则的分配过程,确保正确处理边界情况

L2-1 炼化

排序问题,数据量比较小,可以直接使用冒泡排序进行排序。

#include<stdio.h>
int a[1005];
int n;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<=n-i;j++)
		{
			if(a[j]>a[j+1])
			{
				int t=a[j];
				a[j]=a[j+1];
				a[j+1]=t;
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		printf("%d",a[i]);
		if(i!=n)
		printf(" ");
		else
		printf("\n");
	}
	printf("%d",a[n]);
	return 0;
 }

L2-2 勇闯玉虚宫

本题有两个坑点:

  • 一个就是k的值可能过大,导致越界,所以在暴力遍历数组下标的时候要判断一下会不会越界
  • 另外一个就是最后的和会爆 int 所以要开long long
#include<bits/stdc++.h>

using namespace std;

#define int long long

signed main() {
    int n;
    cin >> n;
    vector<vector<int>> a(n + 1, vector<int> (n + 1));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> a[i][j];
        }
    }
    int x, y, k;
    cin >> x >> y >> k;
    int sum = 0;
    for (int i = x - k / 2; i <= x + k / 2; i++) {
        for (int j = y - k / 2; j <= y + k / 2; j++) {
            if (i <= 0 || i > n || j > n || j <= 0) continue;
            sum += a[i][j];
        }
    }
    cout << sum << endl;
    return 0;
}

L2-3 MyGO!!!!!

首先我们需要搞懂两个下标,一个是有效音符的下标,一个是给定的音符串的下标。搞懂这个,做法就很清晰了。其次,此题目判断质数的时候,不能再暴力到x-1,必须到根号x的位置。

#include <bits/stdc++.h>  
  
using namespace std;  
  
bool isprime(int x) {  
    if (x == 1) return 0;  
    for (int i = 2; i * i <= x; i++) {  
        if (x % i == 0) {  
            return 0;  
        }  
    }  
    return 1;  
}  
  
  
int main() {  
    int n, k;  
    int atleast;  
    cin >> n >> k >> atleast;  
    int idx = 1;  //总音符串中的下标
    int tr = 1;  //实际有效音符下标
    vector<int> arr(n + 1);  
    for (int i = 1; i <= n; i++) cin >> arr[i];  
  
    for (; idx <= n; idx++) {  
        if (arr[idx] != (1 + (tr - 1) * k) * (1 + (tr - 1) * k)) {  
            if (!isprime(arr[idx])) {  
                cout << "Wrong!" << endl;  
                cout << idx << endl;  
                return 0;  
            }  
        } else {  
            tr++;  
        }  
    }  
    if (tr >= atleast) {  
        cout << "It's MyGO!!!!!" << endl;  
    } else {  
        cout << "Useless!" << endl;  
    }  
  
  
}

L2-4 空间优化

本题主要考察对于结构体构造,简单暴力查重能力的考查,在c++语言中class与c语言的struct高度类似,本题用class实现,先开一个class数组存放id和email的信息,然后从头开始遍历将所有email相等的id保留第一个即可

#include <vector>
#include <unordered_map>
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

struct Person {
    int id;
    string email;
};

Person p[1000];

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> p[i].id;
        cin >> p[i].email;
    }
    for (int i = 0; i < n; i++) {
        if (p[i].email != "") {
            for (int j = i + 1; j < n; j++) {
                if (p[i].email == p[j].email) {
                    p[j].email = "";
                }
            }
        }
    }
    for (int i = 0; i < n; i++) {
        if (p[i].email != "") {
            cout << p[i].id << " " << p[i].email << endl;
        }
    }
    return 0;
}

L3-1 服务器性能排行榜

在竞赛中,一般认为每秒可以执行 5×10⁸ 次运算。

本题 n 的数量级达到 10⁵。排序时必须使用时间复杂度为 O(log n) 的排序算法。

删除数据时,不可暴力遍历,需要使用高效的数据结构。用集合或映射能实现最快 O(1) 时间复杂度内删除元素。

C++

以下 main 函数的前两行代码可以加快读入数据速度,是竞赛常用技巧。

#include <algorithm>
#include <iostream>
#include <unordered_set>
#include <vector>
using namespace std;

struct Server {
    int id;
    int responseTime;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    vector<Server> servers;
    for (int i = 0; i < n; i++) {
        int id, responseTime;
        cin >> id >> responseTime;
        servers.push_back({id, responseTime});
    }

    int m;
    cin >> m;
    unordered_set<int> toDelete;
    for (int i = 0; i < m; i++) {
        int id;
        cin >> id;
        toDelete.insert(id);
    }

    // 过滤掉需要删除的服务器
    vector<Server> remaining;
    for (const auto &s : servers) {
        if (toDelete.find(s.id) == toDelete.end()) {
            remaining.push_back(s);
        }
    }

    // 按照响应时间升序,若相同则按 ID 升序
    sort(remaining.begin(), remaining.end(),
         [](const Server &a, const Server &b) {
             return a.responseTime == b.responseTime
                        ? a.id < b.id
                        : a.responseTime < b.responseTime;
         });

    // 输出排序后服务器 ID
    for (const auto &s : remaining) {
        cout << s.id << "\n";
    }

    return 0;
}

Python

在 Python 中,多次调用 `input()` 函数速度较慢,因此一次读入所有数据并转换成数字。

import sys

input = map(int, sys.stdin.read().split())

n = next(input)
servers = {}
for _ in range(n):
    id = next(input)
    time = next(input)
    servers[id] = time  # 在 servers 中保存 id:time 映射

m = next(input)
for _ in range(m):
    del_id = next(input) # 读入需要删除的 ID
    servers.pop(del_id, None) # 删除 del_id 

# 对于一对 id:time 优先以 time 排序,其次是 id
sorted_servers = sorted(servers.items(), key=lambda x: (x[1], x[0]))

# 输出答案
sys.stdout.write("\n".join(str(id) for id, _ in sorted_servers))

L3-2 最小的数字

本题暴力遍历每个操作的话会超时,但是我们可以发现一个性质:

  • 当改变的值包括最小值的时候,最小值-- / ++
  • 当改变值不包括最小值的时候,最小值不变。
  • 而又因为每次操作数字只会改变1,所以当第二大的数字被操作时,即使它--,也不会比最小的数字更小。

所以综上,我们只需要维护最小的数字看它是否增加或者减少

#include<bits/stdc++.h>

using namespace std;

signed main() {
    int n, m;
    cin >> n >> m;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    int Min = *min_element(a.begin() + 1, a.end());
    while(m--) {
        int op, l, r;
        cin >> op >> l >> r;
        if (Min >= l && Min <= r) Min += op;
        cout << Min << endl;
    }

    return 0;
}

L3-3 Just Monika

宽度优先搜索(BFS)。在bfs中,我们每次往四周走一层,在这样的前提下第一次到达某个点(x,y)距离一定是最近的。前提是每条路线的长度都一样。

我们用队列来存储每次延申的时候四个方向的点。由于要求字典序最小,故按照上下左右四个方向把待搜索的点加入队列。用vis来保证一个点只搜索一次,用prev来记录每个点的前驱位置,用prevDir来记录从哪个方向转到x y这个点。

#include <bits/stdc++.h>  
  
using namespace std;  
#define int long long  
#define pii pair<int,int>  
  
int dx[] = {-1, 1, 0, 0};  // 上、下、左、右  
int dy[] = {0, 0, -1, 1};  
int dir[] = {1, 2, 3, 4};  // 1=上, 2=下, 3=左, 4=右  
  
void solve() {  
    int n, m;  
    cin >> n >> m;  
  
    vector<vector<int>> arr(n, vector<int>(m));  
    for (int i = 0; i < n; i++) {  
        for (int j = 0; j < m; j++) {  
            cin >> arr[i][j];  //邻接表存点之间的关系。
        }  
    }  
  
    vector<vector<int>> dist(n, vector<int>(m, 1e9));  
    vector<vector<int>> vis(n, vector<int>(m, 0));  
    vector<vector<int>> prevDir(n, vector<int>(m, -1));  
    vector<vector<pii>> prev(n, vector<pii>(m, {-1, -1}));  
  
    queue<pii> q;  
    dist[0][0] = 0;  
    q.push({0, 0});  
  
    while (!q.empty()) {  
        pii zz = q.front();  
        q.pop();  
        int x = zz.first, y = zz.second;  
  
        if (vis[x][y]) continue;  
        vis[x][y] = 1;  
  
        for (int i = 0; i < 4; i++) {  
            int nx = x + dx[i], ny = y + dy[i];  
            if (nx >= 0 && nx < n && ny >= 0 && ny < m && arr[nx][ny] == 1) {  
                if (dist[nx][ny] > dist[x][y] + 1) {  
                    dist[nx][ny] = dist[x][y] + 1;  
                    q.push({nx, ny});  
                    prev[nx][ny] = {x, y};  
                    prevDir[nx][ny] = dir[i];  //代表到达nx ny这个点是从dir[i]方向转过来的。
                }  
            }  
        }  
    }  
  
    if (dist[n-1][m-1] == 1e9) {  //代表不能到达。
        cout << "Just Monika" << endl;  
        return;  
    }  
  
    string path = "";  
    int x = n - 1, y = m - 1;  
    while (!(x == 0 && y == 0)) {  
        path += char('0' + prevDir[x][y]);  
        tie(x, y) = prev[x][y];  
    }  
    reverse(path.begin(), path.end());  
  
    cout << path << endl;  
    cout << path.size() << endl;  
}  
  
signed main() {  
    ios::sync_with_stdio(false);  
    cin.tie(nullptr), cout.tie(nullptr);  
  
    int T = 1;  
    while (T--) {  
        solve();  
    }  
}
posted @ 2025-03-19 16:38  河南工业大学算法协会  阅读(398)  评论(0)    收藏  举报