本次contest的全部试题使用了C++进行求解!其中
using LL = long long 可以看作 #define LL long long
C++语言学习强迫症 之 取消了using namespace std的写法(虽然比赛时还是写了,原因见C++命名空间冲突)
初始化使用了C++11的列表初始化 -> A{ 0 },可以避免编译器的自动窄化
显示类型转换使用了C++的static_cast<>效果等同于()转换
有人说你这也不C++啊,scanf、printf用的这么溜🤔(原因:scanf、printf 的IO速度比 cin、cout快,某些情况下卡的就是你的IO时间,保留小数位啥的也是printf好用些)
本次contest使用了STL中的string、vector、upper/lower_bound~具体资料可去STL快速入门进行查看学习!
考试周 (5分)
思路
签到题手速题,没啥好说的。按要求输出即可。
代码
#include <cstdio>
int main() {
int A{ 0 }, B{ 0 };
scanf("%d %d", &A, &B);
printf("%d/%.1lf=%d\n", A, (A * 1.0 / B), B);
return 0;
}
真的恭喜你 (10分)
思路
手速题*2,if-else即可。
代码
#include <cstdio>
int main() {
int marks{ 0 };
scanf("%d", &marks);
if (marks >= 90) {
printf("gong xi ni kao le %d fen!\n", marks);
} else {
printf("kao le %d fen bie xie qi!\n", marks);
}
return 0;
}
平均成绩 (10分)
思路
这道题相对前两题稍微复杂些,给人的感觉——前两题应该是5分,这题15分。
但这题也还是非常基础的统计模拟题了~同样的按要求进行计算和输出。
我们可以定义一个sex变量来控制分别往男生/女生的统计变量里加,具体看代码~
代码
#include <cstdio>
int main() {
int nums{ 0 }, male{ 0 }, female{ 0 }, maleCnt{ 0 }, femaleCnt{ 0 };
int sex{ 0 }, mark{ 0 };
scanf("%d", &nums);
for (int i = 0; i < nums; i++) {
scanf("%d %d", &sex, &mark);
// sex为 1 时加到男生统计变量里去
if (sex) {
male += mark;
maleCnt++;
} else {
female += mark;
femaleCnt++;
}
}
int sumMark = male + female;
int sumCnt = maleCnt + femaleCnt;
if (sumCnt == 0) {
putchar('X');
} else {
printf("%.1lf", sumMark * 1.0 / sumCnt);
}
if (maleCnt == 0) {
printf(" X");
} else {
printf(" %.1lf", male * 1.0 / maleCnt);
}
if (femaleCnt == 0) {
puts(" X");
} else {
printf(" %.1lf\n", female * 1.0 / femaleCnt);
}
return 0;
}
古风A+B (15分)
思路
使用string(STL)瞬间变成5分题~
具体步骤:使用std::to_string函数将A + B的和转为string,一个字符输出一行即可。
其实很多题都是看数据范围的~ 如果A + B超过了long long那就是20分+的题了~
数据范围很小,能暴力就暴力吧😏
提供C语言思路,相加后存到字符数组里(取位操作大家都会吧 -> while循环对10取余然后除以10,条件是n > 0)然后再输出,负数的话就记个标志位, 先输出负号
代码
#include <cstdio>
#include <string>
int main() {
int a{ 0 }, b{ 0 };
scanf("%d %d", &a, &b);
std::string str = std::to_string(a + b);
// for-each循环,等同于 for (int i = 0; i < str.length(); i++) printf("%c\n", str[i]);
for (char c : str) {
printf("%c\n", c);
}
return 0;
}
猜近似数字 (15分)
思路
模拟题 + 1, 同样的思路, 使用C++比C要方便得多, 三个步骤:
- 首先将两个字符串的长度进行比较, 长度不相等直接输出"No"
- 接下来比较两个字符串相同位置字符不相同的次数, 大于一次直接输出"No"
- 最后剩下的情况就是一位不同, 将它们相减取绝对值, 如果大于1, 输出"No"
最后剩下的就是符合要求的字符串, 输出"Yes"
代码
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::getline;
bool judge(string& a, string& b) {
int len{static_cast<int>(a.size())};
int cnt{ 0 };
for (int i = 0; i < len; i++) {
if (a[i] != b[i]) {
cnt++;
if (cnt > 1 || (abs((a[i] - '0') - (b[i] - '0'))) > 1) {
return false;
}
}
}
return true;
}
int main() {
string ans, guess;
// getline用来读取一行字符串,包括空格
getline(cin, ans);
do {
getline(cin, guess);
if (guess == "-1") {
break;
}
if (ans.size() != guess.size()) {
cout << "No" << endl;
continue;
}
cout << (judge(ans, guess) ? "Yes" : "No") << endl;
} while (guess != "-1");
return 0;
}
随机输一次 (20分)
思路
还是模拟…emm
两个循环, 一个是总循环, 一个是系统"黑幕"的次数循环, 每次循环结束要输一次, 其他的时候都是赢的,碰到"End"结束总循环, 唯一需要注意的是, 黑幕循环需要使用取模得到相应的循环下标
代码
#include <iostream>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;
string win(string& p) {
if (p == "ChuiZi") {
return "Bu";
} else if (p == "Bu") {
return "JianDao";
}
return "ChuiZi";
}
string lose(string& p) {
if (p == "ChuiZi") {
return "JianDao";
} else if (p == "Bu") {
return "ChuiZi";
}
return "Bu";
}
int main() {
int num{ 0 };
cin >> num;
vector<int> v(num);
for (int i = 0; i < num; i++) {
cin >> v[i];
}
getchar();
string player;
int temp{ 0 }, index{ 0 };
while (true) {
temp = v[index % num];
index++;
for (int i = 0; i < temp; i++) {
getline(cin, player);
if (player == "End") {
return 0;
}
cout << win(player) << endl;
}
getline(cin, player);
if (player == "End") {
break;
}
cout << lose(player) << endl;
}
return 0;
}
阶乘的非零尾数 (20分)
思路
比赛的时候先去做最后一题了,最后仔细读题后只剩下几分钟了,于是没有完成(其实也还是不太会)
赛后重新买了一次机会, 看了大佬的题解, 才磨出来…真的, 感觉这题难度和前面的题不在一个档次(因为很令人迷惑,调了好久才发现测试点在卡哪里的代码)
坑点在代码中标注出来…还是没太get到这题要我们干嘛
这题可以分为两题的组合:
- n的阶乘的最后k位数, k位数可以有k - 1个前导0而不能有一个尾随0 [巨坑]
- 经典面试/数学题, n的阶乘最后有几个尾随0 (与因子5有关,实在不理解可以记住这个代码片) [不坑]
代码
#include <cstdio>
// 咱也不敢问,LL = long long时是不能AC的
using LL = __int128;
/**
不敢问 +1, 题目给出的最大k是9, 按道理对 1e10 取余就可以了,结果它卡了最后一个测试点(1分)
这里试过e后面的指数取10~15, 唯独13可以AC, 其他都不可
*/
const LL MOD = 1e13;
// 记住结论就行
int getZeroCnt(int num) {
int cnt = 0;
while (num > 0) {
num /= 5;
cnt += num;
}
return cnt;
}
/**
输出k位数, 试过转string补零等操作, 不可AC [坑]
照搬大佬的递归输出
*/
void printNum(LL num, int index, int k) {
if (index == k) {
return;
}
printNum(num / 10, index + 1, k);
putchar(num % 10 + '0');
}
int main() {
int n{ 0 }, k{ 0 };
LL num{ 1 };
scanf("%d %d", &n, &k);
/**
0 < n < 1e7, 求n的阶乘, C++没有大数(其实也没必要, 好像特别大的数只需要关注需要
保留的位数即可, 但自己还是不太明白, 数学什么的好难啊!), 比如结果对 1e9 + 7 取余
*/
for (int i = 1; i <= n; i++) {
num *= i;
// 去掉尾随0
while (num % 10 == 0) {
num /= 10;
}
num %= MOD;
}
printNum(num, 0, k);
printf(" %d\n", getZeroCnt(n));
return 0;
}
三足鼎立 (25分)
思路
是它! 是它! 就是它! 比赛中做的最后一题~ 暴力骗分🤔给了16分, 蛮厚道的了~
花里胡哨的描述可以简化为:给定一条边,求其他可以组成三角形的两边组合。
大家都知道,组成三角形的边数需要满足任意两边之和大于第三边,这条定义可以简化为较小两边之和大于最大边即可组成三角形。
后面去leetcode找到了三角形选边的问题,可用二分、双指针来做,可leetcode是任意选三边,这道题是固定了一边,求另外两边。在草稿纸上写写画画,想用双指针来做,但是不可,因为固定的那条边可以是最大的边,也可以是小边中的任意一条…如果头铁写的话那就相当于枚举了,头痛。。。
最终在大佬题解的帮助下,懵懵懂懂的A了这道题,具体思路为使用二分,直接调用STL的upper/lower_bound,做差统计即可,还有一些小细节代码里说。
代码
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
using LL = long long;
using std::vector;
using std::upper_bound;
using std::lower_bound;
int main() {
int num{ 0 }, a{ 0 };
// 计数要long long,大佬说是数据范围 1e5 导致的int存储可能会爆
LL cnt{ 0L };
scanf("%d %d", &num, &a);
// 要多申请一个空间,也就是下标0不做存储操作,后面二分有用
vector<int> v(num + 1);
for (int i = 1; i <= num; i++) {
scanf("%d", &v[i]);
}
// 避开下标0的排序
sort(v.begin() + 1, v.end());
/**
A 为固定的那条边
right = 不满足 A + B > C 的最小下标
left = 满足 B - A < C 与 A - B < C 的最大下标
下标 0 的空位貌似可以使得这两个函数的搜索避免 B 和 C 的重复
*/
for (LL i = 1; i <= num; i++) {
LL left = static_cast<LL>(upper_bound(v.begin() + i + 1, v.end(), abs(a - v[i])) - v.begin() - 1);
LL right = static_cast<LL>(lower_bound(v.begin() + i + 1, v.end(), a + v[i]) - v.begin() - 1);
cnt += right - left;
}
printf("%lld\n", cnt);
return 0;
}