2020 年 6 月青少年软编等考 C 语言二级真题解析

目录

  • [T1. 计算矩阵边缘元素之和](#T1. 计算矩阵边缘元素之和)
  • [T2. 最长最短单词](#T2. 最长最短单词)
  • [T3. 啤酒厂选址](#T3. 啤酒厂选址)
  • [T4. 统计误差范围内的数](#T4. 统计误差范围内的数)
  • [T5. 单词排序](#T5. 单词排序)

T1. 计算矩阵边缘元素之和

题目链接:SOJ D1254

输入一个整数矩阵,计算位于矩阵边缘的元素之和。所谓矩阵边缘的元素,就是第一行和最后一行的元素以及第一列和最后一列的元素。

时间限制:1 s

内存限制:64 MB

  • 输入
    第一行分别为矩阵的行数 m m m 和列数 n n n, m < 100 m < 100 m<100, n < 100 n < 100 n<100,两者之间以一个空格分开。
    接下来输入的 m m m 行数据中,每行包含 n n n 个整数,整数之间以一个空格分开。

  • 输出
    输出对应矩阵的边缘元素和。

  • 样例输入

    a 复制代码
    3 3
    3 4 1
    3 7 1
    2 0 1
  • 样例输出

    a 复制代码
    15

思路分析

此题考察嵌套循环,属于入门题。

根据题目描述,我们应该定义一个二维数组进行存储与求解。事实上,只需要判断当前输入的数据是否处于矩阵边缘即可,无需二维数组,详见参考代码。

cpp 复制代码
/*
 * Name: T1.cpp
 * Problem: 计算矩阵边缘元素之和
 * Author: Teacher Gao.
 * Date&Time: 2024/11/16 00:06
 */

#include <iostream>

using namespace std;

int main(){
	int m, n, x, sum = 0;

	cin >> m >> n;
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; j++) {
			cin >> x;
			if (i == 1 || j == 1 || i == m || j == n) {
				sum += x;
			}
		}
	}
    
    cout << sum;
    
    return 0;
}

T2. 最长最短单词

题目链接:SOJ D1225

输入 1 1 1 行句子(不多于 200 200 200 个单词,每个单词长度不超过 100 100 100),只包含字母、空格和逗号。单词由至少一个连续的字母构成,空格和逗号都是单词间的间隔。

试输出第 1 1 1 个最长的单词和第 1 1 1 个最短单词。

时间限制:1 s

内存限制:64 MB

  • 输入
    一行句子。

  • 输出
    两行输出:
    第 1 1 1 行,第一个最长的单词。
    第 2 2 2 行,第一个最短的单词。

  • 样例输入

    a 复制代码
    I am studying Programming language C in Peking University
  • 样例输出

    a 复制代码
    Programming
    I
  • 提示
    如果所有单词长度相同,那么第一个单词既是最长单词也是最短单词。

思路分析

此题考察字符串操作与打擂台思想,难度入门。

此题与 2022 年 3 月二级 T3 类似,利用 cin 无法读取空格这个事实,我们迅速解决不定项输入的问题。接下来只需要考虑当前字符串中是否存在逗号,如果存在逗号就将单词进行分隔,然后用每一个单词的长度参与两次打擂台即可。

cpp 复制代码
/*
 * Name: T2.cpp
 * Problem: 最长最短单词
 * Author: Teacher Gao.
 * Date&Time: 2024/11/15 22:56
 */

#include <iostream>
#include <string>

using namespace std;

int main()
{
	string s, Min(105, 'a'), Max = "";
	while (cin >> s) {
        int p = s.find(',');
        while (p != -1) {
            if (p > Max.size()) {
                Max = s.substr(0, p);
            }
            if (p < Min.size()) {
                Min = s.substr(0, p);
            }
            s.erase(0, p + 1);
            p = s.find(',');
        }

		if (s.size() > Max.size()) {
			Max = s;
		}
		if (s.size() < Min.size()) {
			Min = s;
		}
	}

	cout << Max << endl << Min;

	return 0;
}

T3. 啤酒厂选址

题目链接:SOJ D1428

海上有一个岛,在环海边上建有一条环岛高速公路 ,沿着公路有 n n n( 5 ≤ n ≤ 10000 5 \le n \le 10000 5≤n≤10000)个居民点,假设每个居民点有一个编号,从 0 0 0 开始,按顺时针依次从小到大(即, 0 , 1 , . . . , n − 1 0,1,...,n-1 0,1,...,n−1)编号。

在岛上啤酒很受青睐。某啤酒企业计划在岛上投资建一个啤酒厂,并根据啤酒需求每天向居住点送啤酒。已知两个相邻的居民点的距离以及每个居住点每天的啤酒需求量(假设每个居住点每天不超过 2000 2000 2000 桶)。假定每单位长度的路程送一桶啤酒需要的费用恒定(为单位费用: 1 1 1)。请问,选择哪一个居民点建啤酒厂,才能使每天送啤酒的费用最小(空车不计费用)。

时间限制:1 s

内存限制:64 MB

  • 输入
    第一行:为居民点数目 n n n。
    后面为 n n n 行,每行为一个居民点的啤酒需求量以及按顺时针离下一个居民点的距离(距离为不超过 100 100 100 的正整数),从编号为 0 0 0 的开始,按递增顺次给出。
    注意:后面第 n n n 行对应于居民点 ( n − 1 ) (n-1) (n−1) 的啤酒需求量以及到编号为 0 0 0 的居民点距离。

  • 输出
    啤酒厂所在的居民点编号以及每天的运输费用,其间以空格分隔,数据保证答案唯一。

  • 样例输入

    a 复制代码
    6
    500 10
    300 30
    350 25
    400 60
    700 28
    200 35
  • 样例输出

    a 复制代码
    0 94100

思路分析

此题考察枚举法,属于基础题。

依次枚举啤酒厂的位置,然后计算每个居住点的运输成本,最后在所有运输成本中选取最小值即可。计算居住点的运输成本时应该选择两个运输方向中,运输距离较短的一个。注意题目给出的距离是相邻两个居住点的距离,因此计算运输距离时需要累加。该算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。

cpp 复制代码
/*
 * Name: T3_1.cpp
 * Problem: 啤酒厂选址
 * Author: Teacher Gao.
 * Date&Time: 2026/01/10 02:17
 */

#include <iostream>

using namespace std;

int main() 
{
    ios::sync_with_stdio(false), cin.tie(0);
	
    int n, a[10005] = {0}, d[10005] = {0};
    int sumlen = 0;

    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i] >> d[i];
        // 计算环线总长度
        sumlen += d[i];
    }

    long long ans = 1LL << 60, ansid = 0;
    // 枚举啤酒厂的位置
    for (int i = 0; i < n; i++) {
        int index, len = 0;
        long long cnt = 0;
        // 依次计算每一个居住点的运输成本
        for (int j = i; j < n+i; j++) {
            index = j;
            // 居住点编号修正
            if (j >= n) index = j - n;

            cnt += min(len, sumlen - len) * a[index];
            // 计算下一个居住点的运输距离
            len += d[index];
        }

        if (cnt < ans) {
            ans = cnt;
            ansid = i;
        }
    }
	
    cout << ansid << " " << ans << endl;

    return 0;
}

注意到随着啤酒厂的位置向右移动,啤酒厂左侧的居住点到啤酒厂的距离如果超过环线总长度的一半 ,那么该居住点就应该从啤酒厂的右侧运输。换言之,随着啤酒厂的位置向右移动,最左侧居住点的编号和最右侧居住点的编号都会向右单向移动,适用于尺取法。

为了保持这种单向移动,我们可以把输入数据连续存储两遍,于是 a [ n ] a[n] a[n] 的下一个就是 a [ 1 ] a[1] a[1]。这样一来,居住点的编号就不再需要修正,运输成本的计算就变成了求区间和的操作,适用于前缀和。至此,我们得到了一个 O ( n ) O(n) O(n) 时间复杂度的算法。

首先假设居住点 0 0 0 在啤酒厂左侧,居住点 n − 1 n-1 n−1 在啤酒厂右侧,于是我们可以找到这种情况下啤酒厂的位置 p p p,并且计算出对应的运输成本 t o t tot tot。记 s = 0 s=0 s=0, t = n − 1 t=n-1 t=n−1 分别表示目前啤酒厂两侧距离啤酒厂最远的居住点编号,接下来让啤酒厂向右移动一个位置,然后检测 s s s 号居住点到新酒厂的距离是否超过了环线总长度的一半,如果是的话就将该居住点移动到酒厂右侧,即 s + 1 s+1 s+1, t + 1 t+1 t+1,直到 s s s 号居住点到酒厂的距离不超过环线总长度的一半。

在移动啤酒厂和居住点的过程中更新运输成本 t o t tot tot,并求出每次更新之后 t o t tot tot 的最小值即为答案,具体来说

  • 原来啤酒厂右侧的居住点(编号 p ∼ t p\sim t p∼t)运输成本降低;
  • 从左侧移动到右侧的居住点的运输成本需要重新计算,先减去移动之前的,再加上移动之后的;
  • 依然留在左侧的居住点的运输成本增加。
cpp 复制代码
/*
 * Name: T3_2.cpp
 * Problem: 啤酒厂选址
 * Author: Teacher Gao.
 * Date&Time: 2026/01/10 02:17
 */

#include <iostream>
#include <cmath>

using namespace std;

int n, a[20005], d[20005];
long long suma[20005], sumd[20005], DIS;

int main() 
{
    ios::sync_with_stdio(false), cin.tie(0);
	
    cin >> n;
    for (int i = 0; i < n; i++) {
        // 距离 d 的存储做了下标偏移,目的是便于之后计算距离成本,防止下标越界
        cin >> a[i] >> d[i+1];
        DIS += d[i+1];
        a[i+n] = a[i], d[i+n+1] = d[i+1];
    }

    sumd[1] = d[1]; suma[0] = a[0];
    for (int i = 2; i <= n + n; i++) {
        sumd[i] = sumd[i-1] + d[i];
        suma[i-1] = suma[i-2] + a[i-1];
    }
    
    // 寻找第一个建啤酒厂的位置
    long long tot = 0, p = 0;
    while (sumd[p+1] < (DIS + 1) / 2) p++;
    for (int i = 0; i < n; i++) {
        tot += a[i] * abs(sumd[p] - sumd[i]);
    }

    long long ansid = p, ans = tot, s = 0, t = n-1;
    for (int i = 1; i < n; i++) {
        // 枚举新啤酒厂的位置
        int np = p + i;
        // 新啤酒厂右边的居住点运输成本降低
        tot -= d[np] * (suma[t] - suma[np-1]);
        // 把原啤酒厂左边的居住点中,到新啤酒厂距离超过环长一半的居住点改为从右侧运输
        while (sumd[np] - sumd[s] > (DIS + 1) / 2) {
            // 减去原来的运输成本
            tot -= a[s] * (sumd[np-1] - sumd[s]);
            s++, t++;
            // 计算新的运输成本
            tot += a[t] * (sumd[t] - sumd[np]);
        }
        // 剩下的依然留在左边的居住点运输成本增加
        tot += d[np] * (suma[np-1] - suma[s-1]);

        if (tot < ans) {
            ans = tot;
            ansid = np;
        }
    }

    cout << ansid % n << " " << ans << endl;

    return 0;
}

T4. 统计误差范围内的数

题目链接:SOJ D1199

统计一个整数序列中与指定数字 m m m 误差范围小于等于 x x x 的数的个数。

时间限制:1 s

内存限制:64 MB

  • 输入
    输入包含三行:
    第一行为 n n n,表示整数序列的长度, n ≤ 100 n \le 100 n≤100;
    第二行为 n n n 个整数,整数之间以一个空格分开;
    第三行包含 2 2 2 个整数,为指定的整数 m m m,误差范围 x x x。

  • 输出
    输出为 n n n 个数中与指定数字 m m m 误差范围小于等于 x x x 的数的个数。

  • 样例输入

    a 复制代码
    5
    1 2 3 4 5
    3 1
  • 样例输出

    a 复制代码
    3
  • 提示
    样例中 2 2 2, 3 3 3, 4 4 4 都满足条件,故答案为 3 3 3。

思路分析

此题考察一维数组的查找与统计,属于入门题。

此题与 2021 年 12 月二级 T1 类似,将给定数据存储在数组中之后,依次遍历每一个元素,检测其是否介于 m − x ∼ m + x m-x \sim m+x m−x∼m+x 范围内,若是,则计数器变量累加 1 1 1,最后输出计数器变量的值即可。

cpp 复制代码
/*
 * Name: T4.cpp
 * Problem: 统计误差范围内的数
 * Author: Teacher Gao.
 * Date&Time: 2024/11/15 21:40
 */

#include <iostream>

using namespace std;

int main()
{
	int n, a[105], m, x;

	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	cin >> m >> x;

	int tot = 0;
	for (int i = 1; i <= n; i++) {
		if (m - x <= a[i] && a[i] <= m + x) {
			tot++;
		}
	}

	cout << tot;

	return 0;
}

T5. 单词排序

题目链接:SOJ D1266

输入一行单词序列,相邻单词之间由 1 1 1 个或多个空格间隔,请按照字典序输出这些单词,要求重复的单词只输出一次。区分大小写。

时间限制:1 s

内存限制:64 MB

  • 输入
    一行单词序列,长度小于 10000 10000 10000,最少 1 1 1 个单词,最多 100 100 100 个单词,每个单词长度不超过 50 50 50,单词之间用至少 1 1 1 个空格间隔。数据不含除字母、空格外的其他字符。

  • 输出
    按字典序输出这些单词,重复的单词只输出一次。

  • 样例输入

    a 复制代码
    She  wants  to go to Peking University to study  Chinese
  • 样例输出

    a 复制代码
    Chinese
    Peking
    She
    University
    go
    study
    to
    wants

思路分析

此题考察字符串的查找与排序,属于入门题。

string 数组存储所有字符串之后,分别用 sort() 函数和 unique() 函数即可达到按字典序排序并去重的效果。

cpp 复制代码
/*
 * Name: T5.cpp
 * Problem: 单词排序
 * Author: Teacher Gao.
 * Date&Time: 2024/11/16 00:41
 */

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main() {
	int n = 0;
	string s[105];

	while (cin >> s[++n]) ;

	sort(s + 1, s + n);
	int m = unique(s + 1, s + n) - (s + 1);

	for (int i = 1; i <= m; i++) {
		cout << s[i] << endl;
	}

	return 0;
}
相关推荐
知识分享小能手2 小时前
Ubuntu入门学习教程,从入门到精通,Ubuntu 22.04 中安装 Docker 容器 —— 知识点详解(26)
学习·ubuntu·docker
消失的旧时光-19432 小时前
C++ 中的 auto 与 nullptr:不是语法糖,而是类型系统升级
开发语言·c++
专注VB编程开发20年2 小时前
c#Type数组转成字符串的名称
java·开发语言
fpcc2 小时前
跟我学C++中级篇—C++17中的元编程逻辑操作
c++·模板编程
HABuo2 小时前
【Linux进程(五)】进程地址空间深入剖析-->虚拟地址、物理地址、逻辑地址的区分
linux·运维·服务器·c语言·c++·后端·centos
编程饭碗2 小时前
【多线程编程】
java·开发语言
开开心心_Every2 小时前
安卓做菜APP:家常菜谱详细步骤无广简洁
服务器·前端·python·学习·edge·django·powerpoint
wdfk_prog2 小时前
WIN11如何可以安装ISO
linux·笔记·学习
Darkershadow2 小时前
蓝牙学习之Provision(2)
学习·蓝牙·ble·mesh