郑州轻工业大学-23级新生C语言周赛(1)题目解析

比赛信息

比赛名称:23级新生C语言周赛(1)

比赛命题:何嘉乐、刘成博、李云浩

比赛平台郑州轻工业大学在线评测系统

参赛对象:郑州轻工业大学2023级新生(包含部分校外新生)

比赛时间:2023.10.29 14:00:00 ~ 17:00:00

比赛类型:周赛

直播回放

题目难度

赛前:简单题目:D、E // 中等题目:A、B、G、I、H // 困难题目:C、F

赛后:简单题目:D、E // 中等题目:A、G、I // 较难题目:B、F、H // 困难题目:C

题目详解

解析:首先判断数组中每个数是否满足不超过h的条件,接着对这个数组排序过后,判断每两个数之间的差值是否相等即可得到答案。

代码如下:

1、C++——vertor数组以及sort函数

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
    int n;
    long long h;
    cin >> n >> h;
    vector<long long> nums(n);
    for (int i = 0; i < n; ++i) {
        cin >> nums[i];
    }
    if (n == 1) {
        cout << "我要易大山啦!" << endl;
        return 0;
    }
    sort(nums.begin(), nums.end());
    if (n == 2) {
        if (abs(nums[1] - nums[0]) <= h) {
            cout << "我要易大山啦!" << endl;
        }
        else {
            cout << "像一直在被优化,没队要的是我" << endl;
        }
        return 0;
    }

    for (int i = 2; i < n; ++i) {
        if (nums[i] - nums[i - 1] != nums[1] - nums[0] || abs(nums[i] - nums[i - 1]) > h) {
            cout << "像一直在被优化,没队要的是我" << endl;
            return 0;
        }
    }
    cout << "我要易大山啦!" << endl;
    return 0;
}

2、C语言——冒泡排序

#include<stdio.h>
int n;
long long a[1010], h;

int main() {
	scanf("%d%lld", &n, &h);
	int flag = 0;
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		if (a[i] > h) flag = 1; // 判断是否小于 h
	}
	for (int i = 1; i <= n; i++) { // 冒泡排序
		for (int j = i + 1; j <= n; j++) {
			if (a[i] > a[j]) {
				long long x = a[i];
				a[i] = a[j];
				a[j] = x;
			}
		}
	}
	long long num = a[2] - a[1];
	for (int i = 3; i <= n; i++) { // 判断每两个数之间的差值是否相等
		if (a[i] - a[i - 1] != a[i - 1] - a[i - 2])
		{
			flag = 1;
			break;
		}
	}
	if (flag) printf("像一直在被优化,没队要的是我\n");
	else printf("我要易大山啦!\n");
}

解析:题目所给的卡牌名为整数1~100,所以定义一个大小为 100 的数组即可储存所有卡牌的所需要的数量以及价格。因为每家店的卡牌数量为无限,因此只需要找到每种卡牌的最低价,再计算价钱即可。

代码如下:

#include <stdio.h>
#include <string.h>
const int INF = 1e9;
int s1[101] = { 0 };
int s2[101];
signed main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		s1[x] = y; // 记录每种卡牌需要的数量
	}
	for (int i = 1; i <= 100; i++)
		s2[i] = INF; // 将每种卡牌的初始单价设为极大值
	while (m--)
	{
		int ka;
		scanf("%d", &ka);
		for (int i = 1; i <= ka; i++)
		{
			int x, y;
			scanf("%d%d", &x, &y);
			if (y < s2[x])
				s2[x] = y; // 更新每种卡牌的最低价格
		}
	}
	int ans = 0;
	for (int i = 1; i <= 100; i++) // 计算价钱
	{
		if (s1[i] == 0) // 为 0 说明不需要买该卡牌,跳过
			continue;
		ans += s1[i] * s2[i];
	}
	printf("%d", ans); // 输出答案,完美结束
}

解析:这一题的主要思路是——先把所需要的卡牌和卡牌店里有的卡牌都记录下来。然后依次枚举每种卡牌,寻找该卡牌在各个卡牌店里的价钱和数量,再计算是否可以买齐该卡牌以及买齐所需的价钱,最后再汇总所有卡牌的价钱得到答案。具体详见代码注释。该题的数据范围十分宽松,因此可以直接采用暴力循环查找的方法。该题用到的有——结构体,string类,以及循环排序。

代码如下:

#include <stdio.h>
#include <string.h>
struct nate
{
	char id[20];
	int a, b;
} s[101][101];
typedef struct nate1
{
	int a, b;//储存卡牌店里的牌的编号-id,价格-a,数量-b
} sta; //用来储存某一种卡牌的价格和数量

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	char s1[201][20]; // 需要购买的卡牌编号
	int s2[201];
	int s3[201];
	for (int i = 1; i <= n; i++)
		scanf("%s %d", &s1[i], &s2[i]);
	for (int i = 1; i <= m; i++)
	{
		int kx;
		scanf("%d", &kx);
		s3[i] = kx;
		for (int j = 1; j <= kx; j++)
		{
			char id[20];
			int a, b;
			scanf("%s %d%d", &id, &a, &b);
			if (b > 3)//因为一家店同一种卡牌最多买 3 张,所以大于 3 的都按 3 计 算
				b = 3;

			strcat(s[i][j].id, id);
			s[i][j].a = a;
			s[i][j].b = b;
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {	//依次计算购买每一种卡牌的价钱 
		char id[20] = {};
		strcat(id, s1[i]);
		sta sc[101];
		int kn = 0, kans = 0;// kn-当前有几家店有当前需要的卡牌,kans - 当前需要的卡牌总数
		for (int j = 1; j <= m; j++) {		//通过枚举把所有店里的当前需要的卡牌找出来
			for (int k = 1; k <= s3[j]; k++) {
				if (strcmp(s[j][k].id, id) == 0) {
					kn++;
					sc[kn].a = s[j][k].a;
					sc[kn].b = s[j][k].b;
					kans += s[j][k].b;
				}
			}
		}
		if (kans < s2[i])//总数小于需要购买的数量,肯定买不齐
		{
			printf("bu yong bao jin bi la !");
			return;
		}
		for (int j = 1; j < kn; j++) {//排序,按照价钱从小到大
			for (int k = 1; k <= kn - j; k++) {
				if (sc[k].a > sc[k + 1].a)
				{
					sta ta = sc[k];
					sc[k] = sc[k + 1];
					sc[k + 1] = ta;
				}
			}
		}
		int kx = s2[i];
		for (int j = 1; j <= kn; j++)//计算价钱
		{
			if (kx > sc[j].b)
			{
				kx -= sc[j].b;
				ans += sc[j].a * sc[j].b;
			}
			else
			{
				ans += sc[j].a * kx;
				break;
			}
		}
	}
	printf("%d", ans);//输出答案, 完美结束
	return 0;
}

解析:最基础的,你只要会CV即可,printf函数

代码如下:

#include<stdio.h>
int main() {
	printf("不翘课也能打 ACM!\n");
	return 0;
}

勘误:在比赛期间,这道题目的题面有些瑕疵,题目要求最后输出“这个数至少可以由几个质数组成”,那么如果还按照原解析中的:“偶数可以由两个质数组成,奇数可以由三个质数组成”。示例:7+2=9 就可以推翻这个结论。

现在OJ官网已经修改了题面:最后的要求由“这个数至少可以由几个质数组成” 修改为 “这个数至少可以由几个奇质数组成”,请各位注意。

题目看起来很复杂,其实照着结论判断给定的数是奇数还是偶数即可,偶数的话就输出 2,奇数的话就输出 3。要注意的是:题面中要求的是可以由几个奇质数组成,那么此时7+2=9就不成立了。

结论:偶数可以由两个奇质数组成,奇数可以由三个奇质数组成。

代码如下:

#include<stdio.h>
int main() {
	int n;
	scanf("%d", &n);
	if (n % 2 == 0) printf("2\n");
	else printf("3\n");
	return 0;
}

如果按照原题目来解决的话,我们只需要加一步判断:判断 n-2 是否是质数,如果是质数,那么就输出2;如果不是质数,就输出3。同样可以应对 7+2=9 这种情况。

代码如下:

#include<stdio.h>
bool is_prime(int n)
{
    if(n==1)
        return 0;
    for(int i=2;i<=n/i;i++)
    {
        if(n%i==0)
            return 0;
    }
    return 1;
}
int main() {
	int n;
	scanf("%d", &n);
	if (n % 2 == 0) 
		printf("2\n");
	else {
        if (is_prime(n - 2))
            printf("2\n");
        else
            printf("3\n");
	}
	return 0;
}

解析:这种题目属于构造题,需要分情况讨论:

当 n=1 时,只要求 k>=1 即可满足条件;当 n>=2 时,我们可以发现,最优策略是一大一小的排列,这样可以让每两个相邻的数字相加的和的最大值尽可能最小,类似于 n=6 时,输出 6 1 5 2 4 3,此时只要保证k>=n+1 即可,当然也可以最大数字在最右边 3 4 2 5 1 6,这样也满足条件。

代码如下:

#include<stdio.h>
int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int n, k;
		scanf("%d%d", &n, &k);
		if (n == 1)
		{
			if (k >= 1)
				printf("1 ");
			else
				printf("-1");
			printf("\n");
			continue;
		}
		if (k < n + 1)
			printf("-1\n");
		else
		{
			int x = 1, y = n;
			for (int i = 1; i <= n; i++)
			{
				if (i % 2)
					printf("%d ", y--);
				else
					printf("%d ", x++);
			}
			printf("\n");
		}
	}
	return 0;
}

解析:使用贪心思想想一下,想让 lyh 拿的数量在每一个时刻都多于 hjl 的话,就让袋子里边是偶数个的全部排列在序列前边,先让 lyh 拿完可以拿的全部小水怪,剩余袋子里小水怪的数量就全是奇数了,然后 hjl 拿走剩下的小水怪,所以只要让袋中是偶数数量的小水怪总量之和大于袋子中是奇数数量的小水怪数量之和就可以满足 lyh 手中的水怪数量一直大于 hjl 手中的水怪数量。只需要分别对奇数和偶数求和就行。

代码如下:

#include<stdio.h>
void solve() {
	int n;
	scanf("%d", &n);
	int odd = 0, even = 0;
	for (int i = 1; i <= n; i++) {
		int x; scanf("%d", &x);
		if (x % 2 == 1) odd += x; //hjl 拿的小水怪数量
		else even += x;//lyh 拿的小水怪数量
	}
	if (even > odd) printf("YES\n");
	else printf("NO\n");
}
int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		solve();
	}
	return 0;
}

解析:简单模拟题,按照题意模拟即可。只需要注意几个限制条件,血量不能大于 10,血量小于 0 直接结束。

代码如下:

#include <stdio.h>
#include <string.h>
int n, jy, lv;
int a, x, hp = 10;
void LV()
{
	int o = 1;
	while (jy >= o) // 经验值大于上限
	{
		jy -= o;
		lv++;  // 等级加 1
		o *= 2; // 经验上限乘 2
	}
}
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d%d", &x, &a);
		hp -= x;     // 血量减少
		if (hp <= 0) // 血量为 0,结束
			break;
		jy += a;     // 先把经验累加, 最后计算等级
		if (hp > 10) // 血量超过上限
			hp = 10;
	}
	LV();                   // 计算等级
	printf("%d %d", lv, jy); // 输出答案,完美结束
	return 0;
}

解析:较为简单的博弈问题,可以发现不管对方拿多少石子,我们可以控制一轮过后总是拿走三颗石子。因此若初始石子对3取模有余数的话,可以保证最后一次一定是 Alice 拿石子,反之则是每轮二人操作后都会拿走 3 颗,Bob 会拿走最后一颗石子。

代码如下:

#include<stdio.h>
int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int n;
		scanf("%d", &n);
		if (n % 3 == 0)
			printf("Bob\n");
		else
			printf("Alice\n");
	}
}

尾言

本文章中大部分解析以及代码来自郑州轻工业大学官方解析,其中部分解析以及代码经过本人修改,如有错误,请及时指出,喵~~!

------本文已结束,感谢您的阅读------
THE END
喜欢就支持一下吧
点赞23 分享
评论 抢沙发
头像
善语结善缘,恶语伤人心
提交
头像

昵称

取消
昵称常用语 夸夸
夸夸
还有吗!没看够!
表情图片

    暂无评论内容