2020级HAUT新生周赛(一)题解
C题一开始忘了加数据,半个多小时才发现,影响了一部分人的罚时,实在抱歉,求轻喷。
小青的班委竞选
注意多组输入,然后一直 if 和 else if 就可以了
#include <stdio.h> int main() { int n; while (scanf("%d", &n) != EOF) //多组输入 { if (n == 1) printf("monday\n"); else if (n == 2) printf("tuesday\n"); else if (n == 3) printf("wednesday\n"); else if (n == 4) printf("thursday\n"); else if (n == 5) printf("friday\n"); else if (n == 6) printf("saturday\n"); else if (n == 7) printf("sunday\n"); } return 0; }
小青的报道
我们首先需要求出n!,然后写一个循环从1到n!依次判断是否满足条件即可。
#include <stdio.h> int main() { int n, sum = 1, ans = 0; scanf("%d", &n); for (int i = 1; i <= n; i++) //求阶乘 sum *= i; for (int i = 1; i <= sum; i++) if (sum % i == 0) //判断是否满足条件并计数 ans++; printf("%d", ans); return 0; }
上面这个是最简朴的方法,我们看这段代码:
for (int i = 1; i <= sum; i++) if (sum % i == 0) //判断是否满足条件并计数 ans++;
和求素数的代码比较相似,所以我们可以按照求素数的方法对他进行一点小小的优化。例如,如果,那么一定有
。如果他们二者不相等的话,我们可以一次找到两个sum的因数。同时我们循环的范围也可以减小,所以我们这段代码可以改成这个样子:
for (int i = 1; i * i <= sum; i++) { if (sum % i == 0) { if (sum / i == i) //加个特判,防止重复加了 ans++; else ans += 2; } }
有了这个小优化的话程序会跑的稍微快那么一点点(没有卡这个小优化,怕被喷)。当然,还有比这更快的方法,叫做算数基本定理,由于篇幅有限(其实是因为菜),不在这里过多介绍,感兴趣的同学可以自主学习一下。
小青的传统艺能
直接输出即可,C语言 printf 输出%需要打两个%。
#include<stdio.h> int main() { printf("%%%%%%zcs"); return 0; }
小青的大学英语
这道题是一个简单的贪心,不知道贪心是什么可以当做没有看到上一句,我们要选择最少的插排个数,显然我们应该优先选取插头多的插排,所以我们应该先按照插头数量从大到小对插排排序,然后依次选取插头数量最大的插排。同时记录当前可以使用的插头数量,如果已经大于需要的插头数,就输出当前已经使用的插排个数同时停止选取。如果直到最后插头数目也不够就依题意输出-1。
#include <stdio.h> int main() { int n, m, k; int shuzu[55]; while (scanf("%d%d%d", &n, &m, &k) != EOF) { for (int i = 0; i < n; i++) scanf("%d", &shuzu[i]); if (k >= m) //不需要插排 { printf("0\n"); continue; } //从大到小排序 for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (shuzu[j] > shuzu[i]) { int t = shuzu[i]; shuzu[i] = shuzu[j]; shuzu[j] = t; } } } int ke = 0, sum = k; //ke作为一个标记 for (int i = 0; i < n; i++) { sum += shuzu[i] - 1; //因为每个插排需要占用一个插头位置,所以需要减一 if (sum >= m) //判断是否已经满足条件 { printf("%d\n", i + 1); ke = 1; //更新标记表示可以满足条件 break; //跳出循环 } } if (ke == 0) //无法达成目标 printf("-1\n"); } return 0; }
小青的高等数学
比赛时因为数据稍微有点问题,好像放过了几个错误算法,赛后会进行数据加强,就不重判了,大家可以把自己代码重新提交一下试一试。
众所周知,2和3都是质数,同时它们也是最小的两个质数,我们可以通过 1 + 1 = 2 和 1 + 2 = 3 来构造出这两个素数,由题意得知 [1 , m] 中所有的整数均在序列中出现过,所以我们在任何时候都可以选取1,2来获得3,同时由于1可能出现多次,我们也可以通过选取两个1来获得2,我们只需要记录1的个数,最后判断一下1的个数是否等于1。如果等于1输出3,如果大于1输出2。
#include <stdio.h> int main() { int n, m, x; scanf("%d%d", &n, &m); int onenum = 0; //表示数据中1的数目 for (int i = 0; i < n; i++) { scanf("%d", &x); if (x == 1) onenum++; } if (onenum == 1) printf("3"); else printf("2"); return 0; }
小青的女装生涯
由于小青和四火的衣服都放入了同一个衣柜里,同时所有的衣服都归小青,所以我们可以把两个序列当成一个序列来操作。由题意可知,我们每次可以使用a,b来让一个新的数a+b加入序列,同时从序列中删除a或b。定义重复的数字数目为序列中数字总个数减去序列中数字种类数,我们每次最多可以让序列中重复的数字数目减少1。例如 1 2 2 4,我们如果使用两个2替换得到序列1 2 4 4,我们序列中重复的数字数目并没有发生变化,所以这种转换是无效的。那么我们该如何选取两个数字使得每次的获得的新数字并不存在于原先的序列中成为我们需要解决的问题。显然,因为序列中每个数都大于0,我们每次都选取一个重复的数字和当前序列中最大的数字得到的新数字一定不存在于原先序列中,与此同时,我们可以舍弃那一个重复的数字。得到的新序列中重复的数字数目就可以稳定的减少1。
例如:
我们惊喜的发现这样选取的话我们操作的次数就恰好是序列中重复的数字数目,于是我们的思路就这么出来了,记录序列中每个数出现的次数(如果数组定义在主函数中,记得数组初始化,写题解时亲身经历的bug)最后从1到100查看序列中每个数的出现次数,如果这个数出现过,我们就加上这个数的出现次数减一,最后输出结果即可。当然,我们也可以理解为女装总数减去种类数,得出答案都是一样的。
#include <stdio.h> int main() { int n, m, x, shuzu[205] = {0}, ans = 0; //注意数组的初始化 scanf("%d%d", &n, &m); for (int i = 0; i < n + m; i++) //因为两个序列可以合成为一个序列(衣服都是小青的),我们可以直接循环n+m次 { scanf("%d", &x); shuzu[x]++; //记录每个数出现次数,shuzu[x]表示x在序列中出现的次数 } for (int i = 1; i <= 100; i++) //每件衣服的漂亮值范围是[1,100] { if (shuzu[i] > 0) //如果这个数出现过 ans += shuzu[i] - 1; } printf("%d", ans); return 0; }
小青的宿舍
这道题和小青的报道差不多,也是写一个循环从1开始逐个计算。我们只需要计算从1到n的每个数的数位上2出现的个数最后累加即可。
#include <stdio.h> int main() { int n; scanf("%d", &n); int ans = 0; for (int i = 1; i <= n; i++) { int cnt = 0, x = i; //cnt代表这个数数位中2的个数,x表示这个数,不直接使用i是因为i是循环计数器,不能随意修改 while (x) //计算某个数中2出现的次数,x不为0时循环会继续进行,等x为0时循环就终止了 { if (x % 10 == 2) //如果当前数字的最后一位是2,我们的计数就加一次. cnt++; x /= 10; //然后把数字的最后一位去掉 } ans += cnt; //加到总数上,因为数据范围原因,最后总数也不大,可以最后再取模 } printf("%d", ans % 2020); //别忘了取模 return 0; }
小青的线性代数
判断阶数然后按照书上的行列式计算方法模拟即可。
一阶行列式求值:
二阶行列式求值:
三阶行列式求值:
#include <stdio.h> int main() { int n, a, b, c, d, e, f, g, h, i; scanf("%d", &n); if (n == 1) { scanf("%d", &a); printf("%d", a); } else if (n == 2) { scanf("%d%d%d%d", &a, &b, &c, &d); printf("%d", a * d - b * c); } else if (n == 3) { scanf("%d%d%d%d%d%d%d%d%d", &a, &b, &c, &d, &e, &f, &g, &h, &i); printf("%d", a * e * i + b * f * g + c * d * h - a * f * h - b * d * i - c * e * g); } return 0; }
小青和四火的肥宅生活
简单来说就是n个数中取k个数让他们的最大公约数最大。
我们假设这k个数的最大公约数为,第k大的数最小应该为
,为尽量保证这k个数在
范围内,显然这k个数应依次为
,故
,易得
,所以
的最大值即为
,直接输出即可。
#include <stdio.h> int main() { int n, k; scanf("%d%d", &n, &k); printf("%d", n / k); return 0; }
小青和nana的不解之谜
我们需要先从小青扔出的色子数来判断小青的变量名,然后输出n遍变量名即可。
#include <stdio.h> int main() { int n; scanf("%d", &n); if (n % 3 == 0) { for (int i = 0; i < n; i++) //输出对应的变量名n遍即可得到文件名 printf("nana"); } else if (n % 3 == 1) { for (int i = 0; i < n; i++) printf("nanana"); } else if (n % 3 == 2) { for (int i = 0; i < n; i++) printf("nananana"); } return 0; }