Accoding做题记录
前言
本文用于记录笔者在北航OJ上的做题记录,将会把一些遇到困难/有趣的题目做法记录下来,供自己复习以及后来者参考。做题过程中有不会的题目参考了北航OJ通关攻略
程设
17级信息类程设
第一次上机
C 你会向上取整嘛?
用ceil函数逃课
F N*3 + 1 problem
使用龟速乘防止爆ll
第一次练习赛
B 你会写月份的英文吗
用std::map存储月份和月份的英文,然后用find函数查找
1 | int main() |
第二次上机
D 成双成对的尾巴
枚举猜出规律,然后打表
E QZZ的世界观测
先对面积排个序,必然是大的包住两个小的,之后的包含便只有8种情况,枚举即可
第二次练习赛
C 逆序对统计
归并排序求逆序对数量,暂时还不会(哈哈,大类招生)
E 阶乘的小尾巴
数学,不会
第三次上机
C TQ有点二
__builtin_popcount只能数32位的,所以用不了
此外,暴力移位会T掉
观察(百度)可得,n每与上n-1一次,1的个数就减一,所以只要不断与上n-1,直到n为0,就可以得到1的个数
第三次练习赛
B 翻转数字
strtoll,atoll,atoi用起来
D 一段楼梯有n级
递推:有n个台阶时,设有count(n)种走法,最后一步走1个台阶,有count(n-1)种走法;最后一步走2个台阶,有count(n-2)种走法。于是count(n)=count(n-1)+count(n-2)。
E 求凸多边形面积
把第一个点作为顶点,此后每两个相邻的点作为底边端点,计算三角形面积,累加即可。三角形面积使用叉乘求解,因为叉乘带正负,所以要套一层abs。
推广:任意多边形?凸包?
第四次上机
C 数字统计
不使用前缀和优化也可过
E 金币
不使用前缀和优化也可过
第四次练习赛
A three investigators
STL字符串的使用还要继续加强
22级信息类程设(不开放)
第一次上机
J 繁琐的识别mini
注意处理多位数和以负号开头的表达式
第一次练习赛
G 字符处理机器
输出一些字符时候要加上反斜杠
I 组三角形
考察博弈论,奇数个数的时候先手必胜,偶数个数的时候后手必胜。
偶数时候后手只需看最大数,先手拿走哪个数,后手就拿走最大数-先手者拿的数,例如21=1+20=2+19…先手者拿20就拿1,这样最后就会剩下一个最大数和一组和等于最大数的数,不能组成三角形。如果先手者拿21,那么最大数变成20=1+19=2+18..中间剩一个10,拿走它,回到前述情况。
奇数时候先手者只需拿走最大数,后手者就会面对偶数的情况时的先手。
J 有理数
一条斜对角线上分子分母和为定值。
除此之外,可以通过分析算出一条对角线上假分数的个数。
第二次上机
J void学英语
哈哈,读入一个数之后记得吸一下换行符啊!不然下一行用gets,cin.getline读入字符串会读入该换行符
windows的换行符是\r\n,linux的换行符只有\n,在windows系统下用gets会吞掉每一行最后面的\r\n,但是linux下用gets只会吞掉最后一个\n
那么问题来了,如果数据是在windows环境下构造的,换行符用的是\r\n,但是服务器是linux,管理员直接把windows下生成的数据没经过任何处理就移动到了linux的服务器里
在oj的测评时,每一行的最后都会多一个\r,所以有时候会稀里糊涂的wa
fgets保留换行符,所以输出时候使用printf就自带换行了
K 作业调度
对于每一个任务单独处理,分为到达时间是否早于上一个任务完成时间讨论。
第二次练习赛
F 五八三十五
熟练掌握把数的每一位取出来的操作:
1 | while (1) { |
G 魔法实验1.0
偶数每次除以2不一定得到偶数,所以对于一个偶数化奇的最优办法是和奇数相加。
因此,存在奇数时,和每一个偶数相加即可。
否则,找到一个为了变成奇数,除以2次数最少的偶数,浓缩成奇数,再和每一个奇数相加。
H 眼睛哥的时间
I 哪吒的水题(一)
J 回文日期
遍历一年中每一个合法的月和日,根据四位数字构造出对应的回文日期。
之后,找到最近的一前一后回文日期。(办法是遍历时候每轮更新)
最后计算相差的天数。
第三次上机
E 翻转正整数
1 | 取出整数a高位示例: a&0xffff0000 |
G void学数学
位运算会超时
直接统计次数,只出现一次的数输出就行了…
还有,多测不清空,爆零两行泪:memset(times, 0, 5010*sizeof(int));
I 反码计算机
先来复习一下三种码的概念:
原码:符号位+数值的二进制表示
反码:正数的反码就是原码,负数的反码是对应正数取反。
补码:正数的补码就是原码,负数的补码是对应正数取反再加1。(没有符号位一说)
根据反码的定义,正数的反码就是原码,负数的反码是对应正数取反。
如果是正数,本身即为反码,如果是负数,就直接对对应的正数取反就能得到反码了。
之后再使用C语言的位运算即可解决本题。
当然同样的,输出的时候也是先判断数字的正负,对于负数,先输出符号,再将数字取反后输出。
充分利用sscanf判断正负号读入:
1 | if (sscanf(a, "-%d", &a_num)) |
J 不可分解的01串
润了
第三次练习赛
C 去买花生
第一个数字根本没用。吃完了之后要用getchar连着吃两次才能接下来读入字符…
D 重炮的进制转换
对每一位十六进制数字,首先将读入的字符型变量转换成对应的整型变量,然后依次取出该整型变量的二进制表示
的后四位并输出即可。
E 十进制数按位与
对个位进行取最小值并输出就可以了,注意处理0,我用了一个栈存的倒序输出,要去除前导0的同时还要注意数字一开始就等于0的情况,这时不用去除0了,直接输出0之后程序结束。
H AsadaShino的分组加密
1 | printf("%04X", a[i]);//打印四位十六进制,字母用大写输出 |
G 二舅也许能治好我的精神内耗
分奇偶讨论,奇数无解。注意到0异或x=x,因此两个0和一个x/2
I 狼了个狼
分解质因数,每个因子只出现一次乘起来就是最小值,至于操作次数要将因子出现次数向上匹配到一个2的幂,然后再判断是否需要扩增倍数。
J ACM组队
用unordered_map存索引再遍历搜索哪个次数为1会超时。
满分做法暂时没有学会
第四次上机
H 式神们夜里不睡觉
通过10进制数为中介完成。
注意负进制的整除取余数,顺次倒写这一过程中余数可能为负数。由于除数一定是负数才能导致这种情况,所以可以从商“借”1,加到余数中使得其为正。
注意我们算余数时候是一直除以进制数,除到商为0为止,但如果一开始就是0则需要特判一下,0在任何进制都是0
I 破损的三角铁
用4根铁棒组成正三角形,则必有两个相等边,另外两根铁棒较短,加和之后相等,注意到5000的数据范围,所以时间复杂度O(n2)可以通过。枚举所有的长度,记numi为该长度木棒的数量。首先选取两根长度为i的木棒,接着二重循环从1枚举j(!!终点是i-j等于j且可以取到这一情况才算结束),则另一根木棒为i-j。接着分i-j是否与j相等讨论,计算出选择的方案数,用乘法原理相乘。不要忘了途中取模。
第四次练习赛
F Cirno的完美位运算教室2022
lowbit运算:x&-x,返回x的二进制表示中最低位的1所对应的数值。
我们知道,一个负数的补码是其绝对值的原码取反加一,有了这个前置知识,这个运算的实现原理
留给读者思考。
有了lowbit运算之后就很好分析了,题目要求的y,需要至少有一位与x都是1,至少有一位与x不同。
- x如果是1,y等于3
- x只有一位1,且这个1不在最低位上,则y等于x+1
- x有多位1,则lowbit值即为所求
G 魔理沙的行窃预兆1
DP,dp[1]=0,从dp[2]开始计算,在前面的格子中,选择能到达的本格的,再取dp的min
H 希卡式打孔纸带·alter
感谢2006 Oshwiciqwq和2109 BenWalker的讲解。
反向思考,题目的两种操作可以反向为:
- 反转
- 把尾部为0的数右移,并在前面补上任意数(因为在原来左移时这些高位是被舍弃的)
考虑把目标数经过一定数量的操作变成1:
- 尾部为0则右移。为什么不选择反转?因为反向时从0翻转到1相当于正向的从1到0,而起点1是有许多0的,只有1需要通过反转制造出来。因此,选择反转会导致操作次数增加。
- 特例:-2的补码为111110,这时只需要取反一次就回到起点000001,需要特判。
- 最终数变成1
- 操作的过程中出现0,在操作的过程中要给次数32后直接结束,否则因为0右移始终得0,会死循环
- 前述右移时,到底补充什么数?考虑正着左移之前的情况,要么最高两位不同,要么最高两位相同。最高两位不同,说明最高位之前取反过一次,但此时这个最高位要消失了,之前的取反就浪费了一次,所以在次数最少的情况下,不会让他不同。
第五次上机
I 隔板问题
递归解决。由于题目要求输出每一种方案,因此需要用全局数组保存方案。为了和下标统一,递归函数的参数最好设置为“当前正在处理第几组”,而不是“还剩几个隔板”,这样递归那一组时候,便能找到对应下标存进去了。