##关于广西大学20级第一次段考的题解和牢骚
前文牢骚
首先要明确,题目不难,甚至可以说是简单,六题除了卡格式和卡时长没有很大的问题。最大的问题是之前的题目出的太简单或者太难,导致中等题做的太少,程序优化次数太少,超时和卡格式现象频出。数据太弱,伪代码都能轻易过关。
其次,在比赛前晚上模题我也感觉难度上略高,但在卡了我们数据的前提下也伪ak了(指有一题数据有问题就没模过)。
最后吧,希望大家认真学习好代码,有自己的一技之长,最好能有一个自己感兴趣的项目(比如算法,比如前端,比如后端,比如数据处理,比如视图,比如游戏),这一切都基于,你的代码是好的,而不只是对的。
正文部分,题解
题一:打印菱形
题意:给你一个方程,让你去模拟一个图像。如果这题放在比赛,五分钟第一个ac不是问题。
现在来讲一下比较有效的思路,第一是在草稿纸上建立坐标系,把这些点画在坐标系里面,思考这个方程函数如何和坐标系联系在一起,如果可以,你会用怎样的办法去建立这个函数。
第二种可能可行的思路,暴力打印,用if语句判定这个点是否落在了一个区间,如果是,那就打印。这个区间就是总长/2-该行的点数,总长/2+该行打印的点数。
很多同学群里提问吧,我个人理解是之前做的题目没有遇到过太多根据题意模拟的题,单纯是打印了一个自己想当然的菱形(这个lcx可能全责),但是永远要明白,只有根据题意模拟的题才是有灵魂的,纯看样例的模拟会出大问题。
代码中没有注释会难以消化所以建议自己先写。
long long sum = 2 + b * (a - 1);//long long 长整型(防爆破)
for (long long i = 1;i <= a;i++) {
for (long long j = 1;j <= sum;j++) {
if (j <= sum / 2 - b * (i - 1) / 2 - 1) printf(" ");
else if (j > sum / 2 - b * (i - 1) / 2 - 1 && j <= sum / 2 + b * (i - 1) / 2 + 1) printf("*");
}
printf("\n");
}
for (long long i = a;i >= 1;i--) {
for (long long j = 1;j <= sum;j++) {
if (j <= sum / 2 - b * (i - 1) / 2 - 1) printf(" ");
else if (j > sum / 2 - b * (i - 1) / 2 - 1 && j <= sum / 2 + b * (i - 1) / 2 + 1) printf("*");
}
printf("\n");//每次都是这种问题
}`
题二:签到题
题意,给你一个数字,判定他是否被9996整除,如果是,打印,如果不是,继续判定,是否被49整除,如果是,打印,如果不是,输出不是的打印。
这题难可能在输出格式上??我估计很多人之前没有接触过这样的打印规则,所以全死了。也可能以前做题太少,或者随手抄抄别人代码就过了,就没管过输出格式。
if (n % 9996 == 0)printf("SurplusYYDS\n");
else if (n % 49 == 0)printf("????\n");
else printf("AWSL\n");
代码也无所谓的,这题确实有手就行。
题三:小小数论题
给你一个函数,找出1-99整数中最接近零的函数值。
巧妙运用double ,fabs,pow基本都能过,关键在于你这些函数会不会背。
同样也是有人问我这个很有意思的问题,用选择排序把一百种情况理论上排序出来(细想可能不会爆,但是非常费时间)
我个人理解的最好思路是直接用两个变量ans和p,前者存最小的数据,后者存最小数据的下标。通过比大小来判定这个数据是否是最小数据,并将这个最小数据的下标存入p中
(仔细想来我的思路已经有点动态规划的意思了)
for (int i = 1;i < 100;i++) {
if (ans > fabs(n * pow(i, 3) + m * i - x)) {
ans = fabs(n * pow(i, 3) + m * i - x);
p = i;
}
}
题四:辗转相减法
辗转相除的弟弟,不过和小模拟结合原以为会卡掉一片,没想到大多数人都还是明白了题意去模拟,说明模拟不难,难的是思路。这就和第一题形成了鲜明的对比了。
这题我讲一下我理解的优化点,可以用while(n!=m)判定,这样只需要考虑m>n和m<n两种请况。也就能用六行代码解决问题。
while (n != m) {
if (n > m) {
printf("%lld-%lld=%lld\n", n, m, n - m);
n -= m;
}
if (n < m) {
printf("%lld-%lld=%lld\n", m, n, m - n);
m -= n;
}
}
题五:第二大数字
这题cm感觉背锅,数据造的有点大
首先题面没有想卡你们的意思,这题确实再高两个数量级也能过。但是如果是我做这题,我肯定不会想到排序,而是用搜索。排序是什么,是把一堆无序的东西变有序,搜索是查找你需要的东西。那么这题就很简单了。什么是你要的呢,你要第二大的数字,那么你怎么知道他是第二大呢?
我们可以理解一下,当我们知道了最大的那个数字,是不是理所当然把那个最大值剔除了就是第二大了呢?那么下次问你找第三大,第四大,是不是原理就很显然了呢?
但其实我们还可以更加高效的去思考,我们一遍一遍找最大值,第二大值,第三大值,为什么不一次就找齐呢?
其实这是我觉得最有意思的一题,因为集训队刚开始很多c++选手的思路都是sort一下就完事了,但我觉得这题的复杂度没必要用sort,反而是on。
听完我吹逼就讲正事,我们可以在输入每一个数的时候比较大小,然后把最大的数字存到max里,但是这时候max原本的数字就变成了第二大的数字,就给了max2;如果有一个数他比max小但是比max2大,那就很显然了啊,他就是我们想的第二大的值。
while (n--) {
scanf("%lld", &m);
if (p < m) {
ans = p;
p = m;
}
else if (ans < m) {
ans = m;
}
}
printf("%lld\n", ans);
题六:字符串输出
有朋友问我,这些题目是不是都要用数组存才能做?我和他这么说,理论上,没有一题必须用数组,但我个人习惯上最后一题还是用了数组做。
先讲一下不用数组的思路,查找c 是否出现,没出现则打印出来,出现了就存进一个新变量里来防止自己忘记。接下来再输入一个数,看看这个数是不是m,如果不是,那就回头把c打印出来,如果是,那就两个都可以清空了,直接自觉地的打印lyf。以此类推。
再讲用数组的思路,先存再查,查了之后不打印,直到出现了cm再打印,并且把lyf打印进去。不过这个思路缺点是最后还要把多余的数组都打印出来,就会麻烦一点。我不讲究,不是字符串选手,有个思路就可,不至于手足无措。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<math.h>
char a[200005];
int main() {
int t;
scanf("%d", &t);
while (t--) {
long long n,m;
long long ans;
long long p;
p = 1;
int flag = 0;
scanf("%lld\n", &n);
for (long long i = 1;i <= n;i++) {
scanf("%c\n", &a[i]);
if (a[i-1]=='c' && a[i] == 'm') {
for (long long j = p;j <= i - 2;j++) {
printf("%c", a[j]);
}
printf("lyf");
p = i + 1;
}
}
for (long long i = p;i <= n;i++) {
printf("%c", a[i]);
}
printf("\n");
}
return 0;
}
给看到最后一题的同学一些奖励,最后一题直接上完整代码,虽然这是写的最垃圾的一题哈哈。很多前面的变量都延用方便快速k题。
总结
给看到这里的小朋友一点奖励,讲一些我半小时a题的心得,也算是集训队第二次比赛总结出来的经验。
打代码首先要自己看得懂,要用一些自己最常用的名字命名,要看个人习惯,有的人打字快而且丢三落四,建议多打几个字母告诉自己这个叫什么。像我这种记不住人名的人就喜欢用abcd(友人A),而且基本上都是同一套东西,n就是循环次数,t就是几个样本。变量abc,数组xy巴拉巴拉。
然后就是头文件的打印,每一题的头文件都是重复的,还不如直接复制粘贴一下,自己存一个板子在电脑里,然后做题的时候复制粘贴一下
其他比如说板子,很多人问我板子是什么,板子其实就是你做题一些封装的函数,比如说gcd(辗转相除法),比如说sort(快速排序),还有像冒泡排序,选择排序,甚至质数筛,质数筛打表(做一张已经打好的质数筛表封装在一个数组里)。这些常用的东西你虽然打多会增加熟练度提高你的码量,甚至可以让你成为首a人(第一个做出那题的人),但我觉得大可不必,真正到了生活中,封装的板子不仅能减少你的记忆量,甚至能给你脑子腾出更多的空间去存储其他算法(你可以理解为大脑作为指针存了地址,指向了书本上的内容)。
而优秀的代码往往是这样封装好的代码盒子,你吃透了这些代码盒子能变得更强,但前提是你有足够的时间去学习其他代码盒子。毕竟代码长路漫漫,不是只有算法是核心。
再讲一点有用的废话,while(t–)代替for(int i=1;i<=t;i++)前者是倒叙,后者是升序,这样的语句又清晰又不容易错,关键在于理解计算机的语法,所以学习语言先学习语法,再学习函数,这是亘古不变的。
while(t--)//从t=你输入的值,到t=1,当t=0时跳出循环(先执行循环)
for(int i=1;i<=t;i++)//此时i从1到t,i最后的结果会被自动清空,因为你的定义有且只在这个for循环语句内。(int规定的)
其他看我的代码大家自己吸收,对于大多数巨佬来说可能只是图一乐,但是更多的蒟蒻需要像我这样勇敢的人献出代码来变得更好。
以后如果有时间,我会陆续更新一些oj上大家能做的题解,来为大家变强贡献绵薄之力,毕竟所有有用的oj都有题解,我们这里因为成立时间太短而难以找到,这种事情得有人做,才会有更多的人做。