2022洛阳师范学院ACM实验室招新竞赛题解

A 萌新签到

题目描述

欢迎大家来参加2022洛阳师范学院ACM实验室新生赛,我们实验室全体学长学姐从暑假一直期盼着你们的到来。

我们的小萌新那么可爱,学长学姐肯定不会为难大家呀!

这一题非常简单,你只需要输出"LYNUACM"就可以了。

输入描述:

输出描述:

LYNUACM

思路&代码

考察printf的简单使用

#include <stdio.h>
int main()
{
	printf("LYNUACM");
	return 0;
}

B 成为一个会a+b的大佬

题目描述

计算a+b

输入描述:

第一行输入一个数据组数n

接下来n行每行输入两个整数

输出描述:

对于每组数据计算a+b的值

输入

2
1 1
2 2

输出

2
4

思路&代码

有n组数据, 且组数已经给出。
因为n是输入时读进来的, 无法在代码中设一个固定值:

// 错误代码
int main()
{
	int n, a, b;
	scanf("%d", &n);
	scanf("%d %d", &a, &b);
	printf("%d\n", a + b);
	scanf("%d %d", &a, &b);
	printf("%d\n", a + b);
	return 0;
}

这种只能过个示例, 而提交后对你程序进行测试时的数据n就不一定是2了。

所以这里需要用到适合确定循环次数时的循环, 即for语句。

for ( init; condition; increment )
{
   statement(s);
}
// init为定义语句, 会被首先执行且只执行一次, 定义循环控制变量
// 第二步会判断 condition 条件语句, 如果满足则继续向下, 不满足则退出循环
// 第三步当且仅当第二步满足时, 执行statement(s)的语句。
// 第四部执行 increment 语句, 对循环控制变量进行更新

故正解代码为:

#include <stdio.h>
int main(void)
{
	int n,a,b;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) // 写成 for(int i = 0; i < n; i++) 在这里都是循环n次
	{
		scanf("%d %d", &a, &b);
		printf("%d\n", a + b);
	}
	return 0;
}

C 伏特加+小可可乐

题目描述

计算a+b

输入描述:

多组数据,每组输入两个整数,当两个整数都为0时表示输入结束

输出描述:

对于每组数据输出两个整数的和

示例1

输入

1 1
2 2
0 0

输出

2
4

思路&代码

这次没说有多少组循环, 即不定次循环问题。

在c/c++语言中, 有一个用来判断输入流是否结束的标识符 EOF(end-of-file), 而 scanf 函数本身就有返回值, 默认其值是 成功输入的数量, 若输入结束则返回EOF, 故我们可以根据这个特性来判断什么时候结束循环。

while(condition)
{
   statement(s);
}
// 第一次进入while时, 先判断 condition, 如果成立再执行循环体
// 执行完循环体后, 再进行判断 condition, 之后重复操

故可以设计出一下的代码:

while(1 == 1) // 1==1 是恒成立的, 故循环只要不主动结束就会一直循环, 称死循环
{
	int flag = scanf("%d", &n);
	if(flag == EOF) // 或者 flag == 1
		break; // 强制结束循环
}

但这个太丑了, 来优化一下:

while(scanf("%d", &n) == EOF) // 或者 == 1
{
	//...
}
//同理, 程序会执行condition的语句, 获取其返回值, 若 == 成立则返回1, 可以继续, 若==不成立则返回0, 结束循环

最终代码为:

#include <stdio.h>
int main()
{
	int a,b;
	while(scanf("%d %d", &a, &b) != EOF && (a || b))
		printf("%d\n", a +b);
	return 0;
}

D 艺博学长的a+b

题目描述

艺博学长来到瓜摊买瓜, 老板把瓜一称,  十百块一斤, 艺博学长感觉不对劲, 怀疑瓜皮是金子做的, 但想了想还是买了。

随后来到银行取钱壹佰块, 发现余额不足。

不禁发问:银行是不是已经破产了,为什么我每次取钱都显示余额不足?

但还是要有这壹佰块, 于是学长跟别人打赌自己能一秒算出所有实数相加,  为了必胜, 学长请你来帮他算算, 作为对照。

这道题就是求两个正实数相加(艺博学姐保证没有负数,全部都是正实数)。

输入描述:

在一行中输入两个数据A和B,中间以空格分隔

输出描述:

输出两个数相加的结果

输入

10.01 23

输出

33.01

输入

10.1 5

输出

15.1

思路&代码

浮点数相加就用浮点数类型的变量来存放。
这里用 floa 或者 double 都可

#include <stdio.h>
int main()
{
	float a,b;
	while(~scanf("%f %f", &a, &b))
		printf("%f\n", a + b);
	return 0;
}

E 乐子人转换成绩

题目描述

小乐乐输入百分制成绩,他想将成绩转换成等级制。转换规则为:90-100为’A’,80-89为’B’,70-79为’C’,60-69为’D’,59以下为’E’。如果输入的成绩不在0-100之间,输出’F’。

输入描述:

一行,一个整数表示的成绩。

输出描述:

一行,转换后的等级。

示例1

输入

78

输出

C

思路&代码

输入一个数判断大小, 直接用if堆出来就行

#include <stdio.h>
int main ()
{
	int n;
	scanf("%d",&n);
	if(n>=90&&n<=100)
		printf("A");
	else if(n>=80&&n<=89)
		printf("B");
	else if(n>=70&&n<=79)
		printf("C");
	else if(n>=60&&n<=69)
		printf("D");
	else if(n>=0&&n<=59)
		printf("E");
	else
		printf("F");
	return 0;
}

注意条件设置, 尽量简洁优雅, 这种模拟题最忌讳混乱的码风, 非常容易出错。

下面介绍另一种方法:
显然每个区间都有一个特性, 90-100的属于A, 80-89的属于B, 70-79的属于C, 那么除了A区间, BCD的成绩都十位上的数不变。

可以使用switch语句来写:

#include<stdio.h>
int main()
{
	int n;
	scanf("%d", &n);
	if(n < 0 || n > 100)
        printf("F\n");
        else
        {
            switch(n / 10)
            {
                case 10:
                case 9:
                    printf("A\n");
                    break;
                case 8:
                    printf("B\n");
                    break;
                case 7:
                    printf("C\n");
                    break;
                case 6:
                    printf("D\n");
                    break;
                default:
                    printf("E\n");
                    break;
            }
        }
	return 0;
}

F 白马!定叫他有来无回

题目描述

假如你是李华,你刚得知你的朋友界徐盛(Jie Xusheng)犯了蜀国疆土,被谋黄忠(Mou Huangzhong)万军取首了。对此你和你的同学们都感到很难过,现在同学们决定让你代表他们给界徐盛写一封慰问信。信的内容如下:
1.所有同学对他都表示同情,打算星期五下午派你为代表去看望他并给他带去他喜欢古锭刀;
2.告诉他谋黄忠的体验期已经过了,让他不要再担心;如果他有什么需要你们做的事情,请他告知;
3.希望他作为时代的眼泪不要过于悲伤,祝愿他早日恢复。

但同学突然想起来时空邮局有个活动, 如果今年是闰年就可以免费送信。

其他人都不想拿出手机百度搜一下, 所以就由你来做了:

给定一个年份,判断这一年是不是闰年。

当以下情况之一满足时,这一年是闰年:

  1. 年份是4的倍数而不是100的倍数;

  2. 年份是400的倍数。

其他的年份都不是闰年。

输入描述:

输入包含一个整数x,表示当前的年份。

输出描述:

输出一行,如果给定的年份是闰年,则输出yes,否则输出no。

输入

2021

输出

no

输入

2008

输出

yes

备注:

说明:当试题指定你输出一个字符串作为结果(比如本题的yes或者no,你需要严格按照试题中给定的大小写,写错大小写将不得分。

思路&代码

判断一个数a是不是数b的倍数, 使用取余运算, 如果 a % b == 0 说明 a 可以被 b整除, 说明b可以通过乘上一个数变成a, 那么a就是b的倍数。

条件判断的且运算符是 &&, 或运算符是 ||

#include <stdio.h>
int main()
{
	int n;
	scanf("%d", &n);
	if((n % 4 == 0 && n % 100 != 0) || (n % 400 == 0))
		printf("yes\n");
	else
		printf("no\n");
	return 0;
}

G 三角形判断

题目描述

KiKi想知道已经给出的三条边a,b,c能否构成三角形,如果能构成三角形,判断三角形的类型(等边三角形、等腰三角形或普通三角形)。

输入描述:

题目有多组输入数据,每一行输入三个a,b,c(0<a,b,c<1000),作为三角形的三个边,用空格分隔。

输出描述:

针对每组输入数据,输出占一行,如果能构成三角形,等边三角形则输出“Equilateral triangle!”,等腰三角形则输出“Isosceles triangle!”,其余的三角形则输出“Ordinary triangle!”,反之输出“Not a triangle!”。

输入

2 3 2
3 3 3

输出

Isosceles triangle!
Equilateral triangle!

思路&代码

更复杂一些的判断

根据你扎实的高中数学基础, 应该能一眼看出来, 等边三角形是等腰三角形的子集, 等腰三角形是三角形的子集, 三角形是图形的子集。

因此, 我们要先判断是否为三角形, 再判断是是否为等边, 最后否为等腰。
假设三角形三个边为 a,b,c。
等边很简单 a==b && b==c 即可
等腰则 a==b || b==c || a==c
是否为三角形则用一条定理 a + b > c 来判断, 任意两边之和大于第三边

#include<stdio.h>
int main()
{
	int a, b, c;
	while(~scanf("%d %d %d", &a, &b, &c))
	{
		if(a + b > c && b + c > a && a + c > b)
		{
			if(a == b && b == c)
			printf("Equilateral triangle!\n");
			else if(a == b ||b == c || a == c)
				printf("Isosceles triangle!\n");
			else
				printf("Ordinary triangle!\n");
		}
		else
			printf("Not a triangle!\n");
		
	}
	return 0;
}

H 玩物丧志

题目描述

这几天恰逢 守望先锋:归来 开服, 栋宇学长作为老玩家肯定不会缺席, 谁知道一玩就上瘾。 这不, 已经忘了怎么把一个乱序数组排序了。 聪明的你一定知道怎么做, 来帮帮学长吧!

输入描述:

每个测试用例第一行一个整数 N(1<=N<=1000 要排序的整数数)

然后第二行中有 N 个整数。

输出描述:

打印排序结果

示例1

输入

8
6 4 5 7 1 2 9 3

输出

1 2 3 4 5 6 7 9

思路&代码

如何将一个数组排成有序递增数组?

我们有很多方法可以选, 冒泡, 选择, 插入, 快排, 归并等等。
这里用最基础的冒泡即可。
建议看视频更好理解冒泡排序法_哔哩哔哩_bilibili

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

如果你会C++, 那么这题更简单:

#include <iostream>
#include<algorithm>
using namespace std;
const int N = 1100;
int a[N];
int main()
{
	int n;
	cin >> n;
	for(int i = 0; i < n;i ++) cin >>a[i];
	sort(a, a +n);
	for(int i = 0; i < n;i ++) cout <<a[i] <<" ";
	return 0;
}

这就是为啥大家都用c++(

I 国庆节

题目描述

“你说得对,但是《国庆节》是当代大学制作发行的一款开放世界冒险游戏,游戏发生在一个被称作“大学校园”的幻想世界,在这里,被神选中的人将被授予“两天节假日”,导引调休之力。玩家将扮演一位名为“大院种”的神秘角色,在自由的旅行中邂逅阉割假期、灵活调休的领导们,和他们一起击败强敌,找回失去的节假日——同时,逐步发掘“纳入寒假”的真相。“

这是一个大学学生发表的评论。

但和你有什么关系呢?洛师放假7天且还可以出去校园呢。你想到。

还是来做道题吧, 嘻嘻。

该任务是打印用“*”组成的直角三角形图案。

输入描述:

多组输入,输入一个整数(2~20),表示直角三角形直角边的长度,即“*”的数量,也表示输出行数。

输出描述:

针对每行输入,输出用“*”组成的对应长度的直角三角形,每个“*”后面有一个空格。

输入

4

输出

*
* *
* * *
* * * *

输入

5

输出

*
* *
* * *
* * * *
* * * * *

备注:

文豪学长的小提示:这一题是多组输入呦,你们会多组输入吗?

如果你参加了2022ACM的暑假训练,你肯定知道,在暑假训练里的第一个问题就是多组输入呦。嘿嘿🤭(忘了改成2022)

思路&代码

如何输出一个正方形矩阵 * ?

#include<stdio.h>
int main()
{
	int n;
	scanf("%d", &n);
	for(int i = 0; i < n;i ++)
	{
		for(int j = 0; j < n; j++)
			printf("* ");
		printf("\n");
	}
	return 0;
}

显然如果是三角形矩阵, 每行的星数就是该行的编号, 第一行一个, 第二行两个, 第三行三个。

故最终代码:

#include<stdio.h>
int main()
{
	int n;
	while(~scanf("%d", &n)) // 记得题目描述, 是多组输入
    {
        for(int i = 0; i < n;i ++)
        {
            for(int j = 0; j <= i; j++)
                printf("* ");
            printf("\n");
        }
    }
	
	return 0;
}

J Apex猎杀想要复仇

题目描述

前一世,明辉学长为猎杀榜的无上至尊,猎杀者

最终惨遭30位大师高手围杀,明辉学长殊死拼搏、以伤换伤。

杀了20人逃到昔日至交府。

却没想到昔日至交好友竟是卑鄙小人背后偷袭。

毁明辉学长至尊红甲、摸明辉学长神器克雷贝尔,甲药全无今生我重生为东部碎片倔强青铜,缺惨遭同门针对,妻子嫌弃。被害跌落悬崖,却因祸得福习得神功。

三十年碎片东,三十年碎片西。

请明辉学长吃肯德基疯狂星期四,听明辉学长慢慢说复仇大计。

但KFC店员说只有能算出水仙花数的人才配在疯狂星期四买汉堡, 怎么办呢!

打印出所有的“水仙花数”,所谓“水仙花数”是指一个3位数,其各位数字的立方和等于该数本身,例如,152是水仙花数,因为153=1×1×1+5×5×5+3×3×3。

输入描述:

输出描述:

输出所有水仙花数,每行输出一个水仙花数。

思路&代码

for循环遍历从100开始到999的所有三位数, 对于每个数判断其是不是水仙花数即可。
对于数n
如何取百位数字? n / 100 即可
如何取十位数字? n / 10 % 10 即可
如何取个位数字? n % 10 即可

#include <stdio.h>
int main()
{
	int a, b, c;
	for(int i= 100; i <= 999; i ++)
	{
		a = i / 100;
		b = i / 10 % 10;
		c = i % 10;
		if(a*a*a + b*b*b + c*c*c == i)
			printf("%d\n", i);
	}
	return 0;
}

K 上课点名

题目描述

在大学里一定不能旷课,一天某个老师去上课用自己的火眼金睛感觉教室里有一个学生没有来,于是他就叫学生们报出自己的学号。

已知这个班上的学号是从1开始连续编号的,并且告诉你这个班上有多少人,想问问你到底是谁没有来。

输入描述:

输入数据共两行,第一行为一个整数N,表示班上的学生数量。

2≤N≤200000

第二行 为一行N-1个整数,表示已经来的学生的学号,学号将按乱序给出。

输出描述:

输出一个整数,为没有来的学生的学号

输入

3
1 3

输出

2

思路&代码

对于 n = 6 时, 我们怎么知道以下这串数字的未出现数:
5 3 1 4 2
很简单瞟一眼就知道, 但如果是100个数呢?
显然我们会遍历一遍这些数, 把出现过的记下来, 剩下的就是没出现的, 按顺序记完之后就是
1 2 3 4 5 这显然就是6没出现。

于是这是第一个思路:排序之后遍历一遍, 判断哪个数与之前数的差值为2, 则该数-1就是答案。

接下来介绍第二个思路:
n最大是200000, 而正常情况下我们能用的最大数组容量是1e8(10的八次方)

众所周知数组有下标和值这两个东西, 对于数组的每个位置都是一对 key:value 键值对。

通常我们都是用下标为索引, 值放数据。
这里可以将下标作为数值, 而值放该数值是否出现过, 0表示未出现, 1表示出现。
5 3 1 4 2
输入5, a[5] = 1
输入3, a[3] = 1
输入1, a[1] = 1
输入4, a[4] = 1
输入2, a[2] = 1

然后再遍历一遍a数组, 对于a[i] == 0 的i就是我们要求的数。

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

无需数组版本

By Alkaid 文豪学长

#include <stdio.h>  
  
int main() {  
	long long n, a;  
	cin >> n;  
	long long sum = n * (n + 1) / 2; //全部学号的和  
	for (int i = 0; i < n - 1; i++)  
	{  
		cin >> a;  
		sum -= a; //减去来的 剩下一个就是没来的  
	}  
	cout << sum;  
}

L 素数回文

题目描述

现在给出一个素数,这个素数满足两点:

1、  只由1-9组成,并且每个数只出现一次,如13,23,1289。

2、  位数从高到低为递减或递增,如2459,87631。

请你判断一下,这个素数的回文数是否为素数(13的回文数是131,127的回文数是12721)。

输入描述:

输入只有1行。

第1行输入一个整数t,保证t为素数。

数据保证:9<t<109

输出描述:

输出一行字符串,如果t的回文数仍是素数,则输出“prime”,否则输出"noprime"。

示例1

输入

13

输出

prime

说明

13的回文数是131,131是素数

输入

17

输出

noprime

说明

17的回文数是171,171不是素数(因子有3)

备注:

素数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数。
素数的回文数为题意中的定义,1331不是素数的回文数。

思路&代码

可以分成两个操作, 得到一个素数的回文数, 再判断该回文数是否为素数。

是否为素数:

// 判断n是否为素数
bool isprime = true;
for(int i = 2; i <= n / i; i++)
{
	if(n % i == 0)
	{
		flag = false;
		break;
	}
}
if(flag)
	printf("yes");
else
	printf("no");

题目条件有一条很奇特, 位数都是从高到低或者从低到高, 似乎可以使用上一题的思路, 声明一个大小为10个的数组, 分别映射到0-9数字是否出现, 若本来是正序, 则逆序遍历时得到的就是后半部分的数。

long long n;
scanf("%d", &n);
int a[10], dex = 1; // dex 是最高位的进制
bool order = true; // true是正序, flase 是逆序
if(n % 10 < n /10 % 10)  // 取个位和十位的数相比较来判断是正序还是逆序
	order = false;
while(n) // 取n每位上的数
{
	a[n%10] = 1;
	n /= 10;
	if(n)
		dex *= 10;
}
int t = 0;
if(order)
{
	for(int i = 0; i <= 9; i++)
		if(a[i])
			t = t * 10 + i;
}
else
{
	for(int i = 9; i >= 0; i--)
		if(a[i])
			t = t * 10 + i;
}
long long res = n * dex + t; // 这个数就是得到的回文数了, 不能用int, 因为回文之后的数有可能大于10的9次方, 超过int的范围, 需要用longlong

最后再进行一下组合:

#include<stdio.h>
int a[10];
int main()
{
	long long  n, dex = 1, t = 0;
	bool order = false;
    scanf("%d", &n);
	if(n % 10 < n /10 % 10)  // 取个位和十位的数相比较来判断是正序还是逆序
	   order = true;
    int temp = n;
	while(n) // 取n每位上的数
	{
		a[n%10] = 1;
		n /= 10;
		if(n)
			dex *= 10;
	}
    n = temp;
	if(order)
	{
		for(int i = 0; i <= 9; i++)
			if(a[i] && i != n%10)
				t = t * 10 + i;
	}
	else
	{
		for(int i = 9; i >= 0; i--)
			if(a[i] && i != n%10)
				t = t * 10 + i;
	}
	long long res = n * dex + t; // 这个数就是得到的回文数了
	// 判断n是否为素数
    //printf("%lld\n", res);
	bool isprime = true;
	for(int i = 2; i <= res / i; i++)
	{
		if(res % i == 0)
		{
			isprime = false;
			break;
		}
	}
	if(isprime)
		printf("prime");
	else
		printf("noprime");
	return 0;
}

也可以把判断素数封装到一个函数里, 这样更好看一些:

#include<stdio.h>
int a[10];

bool isprime(long long x)
{
	for(int i = 2; i <= x / i; i++)
		if(x % i == 0)
			return false;
	return true;
}

int main()
{
	long long  n, dex = 1, t = 0;
	bool order = false;
    scanf("%lld", &n);
	if(n % 10 < n /10 % 10)  // 取个位和十位的数相比较来判断是正序还是逆序
	   order = true;
    long long temp = n;
	while(n) // 取n每位上的数
	{
		a[n%10] = 1;
		n /= 10;
		if(n)
			dex *= 10;
	}
    n = temp;
	if(order)
	{
		for(int i = 0; i <= 9; i++)
			if(a[i] && i != n%10)
				t = t * 10 + i;
	}
	else
	{
		for(int i = 9; i >= 0; i--)
			if(a[i] && i != n%10)
				t = t * 10 + i;
	}
	long long res = n * dex + t; // 这个数就是得到的回文数了
	// 判断n是否为素数
    //printf("%lld\n", res);
	if(isprime(res))
		printf("prime");
	else
		printf("noprime");
	return 0;
}
posted @ 2022-10-08 18:30  EdwinAze  阅读(362)  评论(0编辑  收藏  举报