前言:
大学以来 oi 的比赛基本没参加过,就连网上的线上比赛也是如此。之后要参加 csp,如今打一些线上比赛积累下经验,并总结。
总的来说题目应该算是比较简单(当然对于我来说还是不轻松)。出现的需要自己解决的问题为:
- 着急做题而不好好分析题,甚至不好好看题。
- 做题速度慢,不过这一点也没什么好办法,只能慢慢练了。
接下来总结一下各个题的得失。
试题A:完美车牌
描述
有一些数字可以颠倒过来看,例如0、1、8颠倒过来还是本身,6颠倒过来是9,9颠倒过来看还是6,其他数字颠倒过来不构成数字。
类似的,一些多位数也可以颠倒过来看,比如1061倒过来是901.
假设某个城市的车牌只由6位数字组成,每一位都可以取0到9。请问这个城市最多有多少个车牌180°倒过来恰好还是原来的车牌?
例如:车牌号:886988,倒过来还是886988
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个数字,填写多余的内容将无法得分。
解答:
方法1:这个车牌号只可能包含0,1,8,6,9
于是,当确定了车牌号的前3位,那后3位是唯一的。
所以5 * 5 * 5 = 125就好了。
方法2:
6个for循环嵌套,枚举每一个数字上的值,然后逐一判断,是否满足要求
答案:125
总结:
我用的第二种方法,一开始我就把题没看完整导致一开始做错了,之后反应过来又用的第二种方法。这就是典型的前言中的第一个问题,磨刀不误砍柴工,比赛时就算题目再多也要一道一道做,也要一个一个认真分析。
试题B:完美日期
描述
不知天上宫阙,今夕是何年。
对于完美日期yyyy/mm/dd,wlxsq的定义是:
- 年月日中均没有出现数字4,
- 年月日的数位之和是8的倍数
例如:2020/02/02 就是一个完美日期,没有出现数字4,且数位之和是8的倍数。
wlxsq想知道从2020/02/22开始,第88个完美日期是哪个?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个格式yyyy/mm/dd,在提交答案时直接填写这个日期,注意需要如果答案有前导零则不能忽略,填写多余的内容将无法得分。
解答:
题目比较简单,直接模拟即可,只要认真一点都可以做对。
总结:
在敲代码的过程中我还是很严谨的,但是错在了哪里呢?就是题目给的例子是 2020/02/02,但是让从 2020/02/22 开始算,我看成了还是以前者开始算。这就说明还是读题不认真,所以读题认真才是对自己最负责任的行为。
试题D:完美运算
描述
定义a1:表示数字A对应的三进制数位中1的个数
定义a2:表示数字A对应的三进制数位中2的个数
定义完美运算A○B,如果|a1-a2| = |b1-b2|,A○B的值为1,否则为0.请问,在[1,2020] 区间,有多少对(A,B)的结果为1
例如:A=2,B=3,则a1=0,a2=1,b1=1,b2=0,满足|a1-a2| = |b1-b2|所以A○B的结果为1。
注意,A=2,B=3构成的数对(2,3)与A=3,B=2构成的数对(3,2)算同一对。
更新:(2,2)也算一对哦~
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
解答:
思路简单,即模拟即可,但有些地方需要总结一下。
#include <cstdio>
#include <utility>
#include <algorithm>
using namespace std;
const int RANGE = 2020;
pair<int,int> tools[RANGE + 10];
int num = 0;
pair<int,int> alt(int n)
{
int temp;
int first = 0,second = 0;
while(n > 0){
temp = n%3;
n /= 3;
if(temp == 1) first++;
else if(temp == 2) second++;
}
return make_pair(first,second);
}
bool judge(int A,int B)
{
if(A == B) return true;
else{
if(abs(tools[A].first - tools[A].second) == abs(tools[B].first - tools[B].second))
return true;
else
return false;
}
}
void get_ans()
{
for(int i = 1;i <= RANGE;i++){
for(int j = i;j <= RANGE;j++){
if(judge(i,j)) num++;
}
}
printf("%d",num);
}
void init()
{
for(int i = 1;i <= RANGE;i++){
tools[i] = alt(i);
}
}
int main()
{
init();
get_ans();
return 0;
}
总结:
- 一个最老生常谈,也是最容易避免的问题,那就是认真读题,这才是总结的第四题就已经是我第三次说这个问题了。说明我现在太浮躁,一定要狠下心改正。这道题说(2,3)(3,2)这样的情况算一对,而我没看到,导致结果多了。
- 在统计一个数三进制下 1、2 个数时,因为局部变量没有初始化导致初值成了随机数,这也是自己的习惯不好,一定要改正。因为这种写法表明了我的水平是很低的,这个问题在技术含量上是容易的,但却不一定容易改正,所以关于这种类型的问题一定要下功夫让自己养成好习惯。因为在赛场上这种问题是容易被忽略的,也是不容易发现的,所以很重要。
- 这道题虽然是一道很简单的模拟题,但是我在第一次求解的过程中没有使用散列,导致了重复计算。因为这道题规模不大的缘故,所以无所谓。但是这给我的经验是,即使是使用暴力、模拟等比较直接的方法,在具体的实现过程中也是可以优化的,不剪枝。剪枝是一种艺术,也是我们追求算法的一种品质,即不断地思考,来使得求解有效。这也是需要我在平时需要养成的好习惯。
试题G:JM boy 去爬山
描述
疫情终于终于得到了一定的控制~
JM boy在家宅了又宅,宅了又宅,总算是可以带上口罩出去爬山运动了~
JM boy在爬完山之后,发现自己爬的山像及了一个数列。山是有山峰山谷的,而数列也是可以定义山峰山谷的。
例如,对于某n个数的一种排列,如果不存在任意的i使得A_i > A_{i+1} < A_{i+2},则称该种排列为山峰排列。
JM boy想知道,对于1……n的n个数的数列,该数列有多少种排列是属于山峰排列的。
当然,对于n=1及n=2的情况,肯定是所有的排列都属于山峰排列了。
输入
输入一个整数n,表示数列元素的个数。
输出
输出山峰排列的个数。
样例
输入
1
输出
1
输入
3
输出
4
提示
【样例2解释】
共有以下4种方案: 123、132、231、321
【数据规模】
对于60%的数据 n <= 10
对于100%的数据 n <= 60
解答:
首先分析题目(这很重要),通过读题最重要的是要知道题目问的是什么,将问题转化一下发现其实满足题目要求的数列只能是一段升序数列后跟一段降序数列。也就是说,最大值任取一个点,该点之前为升序序列,该店之后为降序序列。
比如1、2、3、4、5、6,当 6 在第三个位置时,前面的两个数为升序序列,后面的三个数为降序序列。前面两个数通过 C 5 2 C_5^2 C52 选出,那么后面的三个数肯定也就确定了。这样,所有满足条件的排序数位 C n − 1 0 + C n − 1 1 + … … + C n − 1 n − 1 C_{n-1}^0+C_{n-1}^1+……+C_{n-1}^{n-1} Cn−10+Cn−11+……+Cn−1n−1,即由二项式定理得结果为 ( 1 + 1 ) n − 1 (1+1)^{n-1} (1+1)n−1,即 2 n − 1 2^{n-1} 2n−1。
下面给出代码:
#include <cstdio>
typedef long long LL;
//a^b
LL quick_pow(LL a,LL b)
{
LL ans = 1;
while(b > 0){
if(b&1) ans *= a;
a *= a;
b >>= 1;
}
return ans;
}
int main()
{
int n;
scanf("%d",&n);
if(n == 1||n == 2) printf("%d",n);
else printf("%lld",quick_pow(2,n-1));
return 0;
}
总结:
对自己真是有点失望了,没想到这是我这场比赛又一次看错题。题目最后一条特地给了 1、2得情况,而我却一扫而过,错误得把它理解成了特殊情况,认为1、2时答案都为 1,使得一个测试点没过。
多的不说了,一定要冷静下来。
后记:
之后的三道题我没做,其中倒数第三到有了想法,但是没时间了。总的来说这种简单题我认真做是可以作对的,但效率比较低。目前努力的方向就是不怕看题费时间,一定要保证正确率,29号第三次模拟赛,冲啊 !