目录
1.A题(进制位数)
位运算符
符号 | 描述 | 运算规则 |
& | 与 | 两个位都为1时,结果才为1 |
| | 或 | 两个位都为0时,结果才为0 |
^ | 异或 | 两个位相同为1,相异为0 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
第一期
问题描述
十进制整数 2 在十进制中是 1 位数,在二进制中对应 10 ,是 2 位数。
十进制整数 22 在十进制中是 2 位数,在二进制中对应 10110 ,是 5 位数。
请问十进制整数 2022 在二进制中是几位数?
解析
根据常识2^10为1024,那么2^11就是2048,2048>2022,即2^10<2022<2^11。
由2<3<2^2,为两位数;2^4<22<2^5,为五位数可知2022的位数为10+1即11位。
答案:11位。
第二期
请找到一个大于 2022 的最小数,这个数转换成二进制之后,最低的 6 个二进制为全为 0 。
请将这个数的十进制形式作为答案提交。
解析
2022联想到2048(100000000000),for循环从2023开始,找到那个大于 2022 的最小数结束,判断方法:对该整数进行六次右移如果每一次的&(与)1都不为1,即最低的 6 个二进制为全为 0,那么这个数就是那个大于 2022 的最小数。
答案:2048。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int s;
for (int i = 2023;; i++) {
bool flag = false;
for (int k = 0; k < 6; k++) {
if (i >> k & 1) {
flag = true;
continue;
}
}
if (!flag) {
s = i;
break;
}
}
cout << s << endl;
return 0;
2.B题(日期问题)
第一期
问题描述
小蓝每周六、周日都晨跑,每月的 1、11、21、31 日也晨跑。其它时间不晨跑。
已知 2022 年 1 月 1 日是周六,请问小蓝整个 2022 年晨跑多少天?
解析
由题意可知小蓝周末跑,日期位数为1的时候也跑。假设定义x为365天的某一天,那么小蓝周末跑的条件就是x%7==1为周六或x%7==1为周日,定义一个数组,每个元素代表着每个月份的日期,用枚举法判断日期是否为1、11、21、31 ,用count计算晨跑的天数,x随着循环加1,满足x%7==1或x%7==1或判断日期是否为1、11、21、31之一,count加1。
答案:138。
代码实现
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int x = 0, count = 0;
int month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
for (int i = 1; i < 13; i++) {
for (int j = 1; j <= month[i]; j++) {
x++;
if (x % 7 == 1 || x % 7 == 2 || j == 1 || j == 11 || j == 21 || j == 31) {
count++;
}
}
}
printf("%d", count);
}
虽然月份是12个,但是数组定义为13个,把第一个赋值为0,使用时从第二个元素开始,也就是下标为1代表着一月,这样便于理解
执行结果
第二期
问题描述
我们计从 1949 年 10 月 1 日至 1949 年 10 月 2 日为经过了 1 天。请问从 1949 年 10 月 1 日至 2022 年 1 月 1 日经过了多少天?
解析
分两段算,先计算1949 年 10 月 1 日到1950 年 1 月 1 日为31+30+31=92,在计算1950 年 1 月 1 日到2022 年 1 月 1 日,用for循环,判断是否为闰年,如果为闰年加366,不是闰年加365。最后两者相加即为答案。
答案:26390
代码
#include "stdio.h"
int main() {
int sum = 92;
for (int i = 1950; i < 2022; i++) {
if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {
sum += 366;
} else {
sum += 365;
}
}
printf("%d", sum);
return 0;
}
3.C题(数学问题)
第一期
问题描述
小蓝特别喜欢调和级数 S(n)=1/1+1/2+1/3+...+1/n
请问,n 至少为多大时,S(n)>12 ?
解析
直接暴力累加计算,需要注意的是结果涉及到了循环的++运算
- S>0 n=1
- S>1 n=2
- S>2 n=4
- S>3 n=11
- S>4 n=31
- S>5 n=83
- S>6 n=227
- S>7 n=616
- S>8 n=1674
- S>9 n=4550
- S>10 n=12367
- S>11 n=33617
- S>12 n=91380
答案:91380
代码实现
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
double sum = 0;
int i;
for (i = 1; sum <= 12; i++) {
sum += 1.0 / i;
}
printf("%d", i - 1);
return 0;
}
切记,切记,切记,最后输出n的时候要减1,因为在最后一次循环的结尾i又加1,而不满足条件的是i加1之前的值
执行结果
第二期
问题描述
8518 是一个非常特殊的数,如果把这个数看成 16 进制数,它的值为 (8518)16=8*16*16*16+5*16*16+1*16+8=34072,而 34072 正好是 8518 的整数倍。9558 也是这样一个数,当看成 16 进制时是 38232。其实长度为 1 的数 0 到 9 都满足看成 16 进制后是自己的整数倍(1倍)。请问,除开长度为 1 的数,最小的满足这样条件的数是多少?
解析
for循环从10开始往后枚举,直到找到满足条件的数结束,判断方法:现将该数转换为十六进制(对其求余每次除10从尾数求其位数,权重每循环一次乘以16,位数乘权重累加),最后在用这个数求余它本身10进制的数,如果为0,这满足,反之,继续循环。
答案:1038
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int res;
for (int i = 10;; i++) {
int weight = 1;
int ans = 0, sum = 0;
int temp = i;
while (temp) {
sum = temp % 10;
ans += sum * weight;
weight *= 16;
temp /= 10;
}
if (ans % i == 0) {
res = i;
break;
}
}
cout << res << endl;
return 0;
}
4.D题(矩阵问题)
第一期
问题描述
给定一个字母矩阵,如果矩阵中的某个位置不在四条边上,而且该位置上的字母小于其上下左右四个位置的字母,则称为一个山谷。
例如,对于如下矩阵
DDDDD
CADCE
FFFFA
共有两个山谷,位于第二行第二列和第四列。请注意第二行第三列和第三行第五列都不是山谷。
对于如下 3030 行 6060 列的字母矩阵,请问有多少个山谷?
PHQGHUMEAYLNLFDXFIRCVSCXGGBWKFNQDUXWFNFOZVSRTKJPREPGGXRPNRVY STMWCYSYYCQPEVIKEFFMZNIMKKASVWSRENZKYCXFXTLSGYPSFADPOOEFXZBC OEJUVPVABOYGPOEYLFPBNPLJVRVIPYAMYEHWQNQRQPMXUJJLOOVAOWUXWHMS NCBXCOKSFZKVATXDKNLYJYHFIXJSWNKKUFNUXXZRZBMNMGQOOKETLYHNKOAU GZQRCDDIUTEIOJWAYYZPVSCMPSAJLFVGUBFAAOVLZYLNTRKDCPWSRTESJWHD IZCOBZCNFWLQIJTVDWVXHRCBLDVGYLWGBUSBMBORXTLHCSMPXOHGMGNKEUFD XOTOGBGXPEYANFETCUKEPZSHKLJUGGGEKJDQZJENPEVQGXIEPJSRDZJAZUJL LCHHBFQMKIMWZOBIWYBXDUUNFSKSRSRTEKMQDCYZJEEUHMSRQCOZIJIPFION EEDDPSZRNAVYMMTATBDZQSOEMUVNPPPSUACBAZUXMHECTHLEGRPUNKDMBPPW EQTGJOPARMOWZDQYOXYTJBBHAWDYDCPRJBXPHOOHPKWQYUHRQZHNBNFUVQNQ QLRZJPXIOGVLIEXDZUZOSRKRUSVOJBRZMWZPOWKJILEFRAAMDIGPNPUUHGXP QNJWJMWAXXMNSNHHLQQRZUDLTFZOTCJTNZXUGLSDSMZCNOCKVFAJFRMXOTHO WKBJZWUCWLJFRIMPMYHCHZRIWKBARXBGFCBCEYHJUGIXWTBVTREHBBCPXIFB XVFBCGKCFQCKCOTZGKUBMJRMBSZTSSHFROEFWSJRXJHGUZYUPZWWEIQURPIX IQFLDUUVEOOWQCUDHNEFNJHAIMUCZFSKUIDUBURISWTBRECUYKABFCVKDZEZ TOIDUKUHJZEFCZZZBFKQDPQZIKFOBUCDHTHXDJGKJELRLPAXAMCEROSWITDP TPCCLIFKELJYTIHRCQAYBNEFXNXVGZEDYYHNGYCDRUDMPHMECKOTRWOSPOFG HFOZQVLQFXWWKMFXDYYGMDCASZSGOVSODKJGHCWMBMXRMHUYFYQGAJQKCKLZ NAYXQKQOYZWMYUBZAZCPKHKTKYDZIVCUYPURFMBISGEKYRGZVXDHPOAMVAFY RARXSVKHTQDIHERSIGBHZJZUJXMMYSPNARAEWKEGJCCVHHRJVBJTSQDJOOTG PKNFPFYCGFIEOWQRWWWPZSQMETOGEPSPXNVJIUPALYYNMKMNUVKLHSECDWRA CGFMZKGIPDFODKJMJQWIQPUOQHIMVFVUZWYVIJGFULLKJDUHSJAFBTLKMFQR MYJFJNHHSSQCTYDTEAMDCJBPRHTNEGYIWXGCJWLGRSMEAEARWTVJSJBAOIOJ LWHYPNVRUIHOSWKIFYGTYDHACWYHSGEWZMTGONZLTJHGAUHNIHREQGJFWKJS MTPJHAEFQZAAULDRCHJCCDYRFVVRIVUYEEGFIVDRCYGURQDREDAKUBNFGUPR OQYLOBCWQXKZMAUSJGMHCMHGDNMPHNQKAMHURKTRFFACLVGRZKKLDACLLTEO JOMONXRQYJZGINRNNZWACXXAEDRWUDXZRFUSEWJTBOXVYNFHKSTCENAUMNDD XFDMVZCAUTDCCKXAAYDZSXTTOBBGQNGVVPJGOJOGLMKXGBFCPYPCKQCHBDDZ WRXBZMQRLXVOBTWHXGINFGFRCCLMZNMJUGWWBSQFCIHUBSJOLLMSQSGHMCPH ELSOTFLBGSFNPCUZSRUPCHYNVZHCPQUGRIWNIQXDFJPWPXFBLKPNPEELFJMT
解析
枚举法判断每个字符与该字符上下左右四个位置的字母的大小,如果该位置上的字母小于其上下左右四个位置的字母那么计数变量count++。
答案:276
代码实现
#include <iostream>
using namespace std;
int main() {
char str[30][60];
int count = 0;
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 60; j++) {
// scanf("%c", &str[i][j]);
cin >> str[i][j];
}
}
printf("\n");
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 60; j++) {
printf("%c", str[i][j]);
}
}
for (int i = 1; i < 29; i++) {
for (int j = 1; j < 59; j++) {
if (str[i - 1][j] > str[i][j] && str[i + 1][j] > str[i][j] && str[i][j - 1] > str[i][j] && str[i][j + 1] > str[i][j]) {
count++;
}
}
}
printf("%d", count);
return 0;
}
输入流cin无论什么情况下都会忽略tab、空格、回车等分隔符
scanf函数在除
scanf("%c",&char)
之外的所有情况都不会把回车符作为输入字符在输入缓存中读取,但scanf("%c",&char)
也不会读取tab、空格,而是把他们作为分隔符在输入缓存中忽略。
第二期
问题描述
小蓝有一个 30 行 60 列的数字矩阵,矩阵中的每个数都是 0 到 9 之间的数字。现在小蓝想从这个矩阵的第一行第一列画一条折线到第 30 行 60 列,线只能沿水平向右走或竖直向下走,只能在有数字的地方拐弯。小蓝想知道,这样一条线经过的数字的和最大是多少。
174094882455171152761423221685761892795431233411387427793198
650286024865090061389344606618496378829135984076361542097372
601657541200146071777733599818266038012509478351201640618984
143988087783837107349651099683484992553337438088068198972282
890781586124258626539246182119762952003918195325258677229419
698255491250839396799769357665825441616335532825361862146291
503649293440596342887581257444442930778730382520372975343211
325351222640703400531067500454956482168314849207060705673849
265774579830223671554026061117300483012903885770893074783710
083450145620356667677191627276513995926532444279237315785832
411595106453089134746365281031552217482363035280722591085079
053410485925413958279617719034175332412908745680774313630190
429314820559328748143552689295945058801322270313370955837837
939182801848609300876356583948397645861551964542532682663945
625356614462682551015176002433628234343684739800880514363921
982340231989891351425389287014819359798014755509282450440511
590838726938103384801541373585690893606978941566666714061214
952341523168827712604946036245881214982452998386986623826275
782780208928205527678781609589000725521486468983551558405472
149903035076783644195574734088152324666290493119955560594634
905391288186024902215444250421277955403412298227858394469856
607272647132163832860126054679347881638761723785858733108109
249157334220127702410373959720286708183036202841837581704881
367895556630088230650972282944827258473951902831431040790814
079538232104075905120989173307660289899942087873076421916033
622143260549608274076012938515668898707915863945382394851328
164677964192631597026176253407553188801750590935427267220117
591817866992665840378311257621611574856498432538327068011953
631534031790352912617015229051836886166704989498756486878095
690013558017746707412183571476823027885971347137127534455141
解析
线性DP,状态计算为:f[i,j]=max(max(f[i - 1][j], f[i][j - 1])) + v,v是该点的权值。答案为f[30][60]。
答案:592
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 35, M = 65;
int f[N][M];
char s[N][M];
int main() {
for (int i = 1; i <= 30; i++)
scanf("%s", s[i] + 1);
for (int i = 1; i <= 30; i++)
for (int j = 1; j <= 60; j++)
f[i][j] = max(f[i - 1][j], f[i][j - 1]) + s[i][j] - '0';
cout << f[30][60] << endl;
return 0;
}
5.E题(拆分问题)
第一期
问题描述
小蓝有一个 100 行 100 列的矩阵,矩阵的左上角为 1。其它每个位置正好比其左边的数大 2,比其上边的数大 1 。
例如,第 1 行第 2 列为 3,第 2 行第 2 列 为 4,第 10 行第 20 列为 48。
小蓝想在矩阵中找到一个由连续的若干行、连续的若干列组成的子矩阵,使得其和为 2022,请问这个子矩阵中至少包含多少个元素(即子矩阵的行数和列数的乘积)。
解析
首先我们要构造出这个矩阵, 假设这个矩阵为 a[ ][ ]。
- 矩阵的左上角为 1, 那么 a[1][1] = 1。
- 其它每个位置正好比其左边的数大 2,我们可以构造出第一行 a[1][i] = a[1][i - 1] + 2
- 其它每个位置正好比其上面的数大 1,根据第一行可以构造出其他 9行 a[i][j] = a[i - 1][j - 1] + 1
我们要求一个子矩阵的和,考虑到矩阵长宽只有 100,我们可以通过枚举子矩阵的左上角 (i, j)以及右下角 (k, l),我们用最简单的四重循环来枚举,此时枚举的复杂度为O(n^4), 若此时和为 2022 矩阵的元素个数等于行数和列数的乘积,即(k−i+1)×(l−j+1), 对所有枚举结果取最小值。
查询子矩阵的和是否为 2022我们可以通过预处理二维前缀和O(1)查询。
答案:12
代码
#include<bits/stdc++.h>
using namespace std;
int a[150][150] , sum[150][150];
int cal(int x1, int y1, int x2, int y2){
return sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 - 1];
}
signed main()
{
a[1][1] = 1;
for(int i = 2;i <= 100;i++) a[1][i] = a[1][i - 1] + 2;
for(int i = 2;i <= 100;i++)
for(int j = 1;j <= 100;j++)
a[i][j] = a[i - 1][j] + 1;
for(int i = 1;i <= 100;i++){
for(int j = 1;j <= 100;j++)
sum[i][j] = sum[i][j - 1] + a[i][j];
for(int j = 1;j <= 100;j++)
sum[i][j] += sum[i - 1][j];
}
int ans = 100 * 100;
for(int i = 1;i <= 100;i++)
for(int j = 1;j <= 100;j++)
for(int k = i;k <= 100;k++)
for(int l = j;l <= 100;l++)
if(cal(i, j, k, l) == 2022)
ans = min(ans,(k - i + 1) * (l - j + 1));
cout << ans << '\n';
return 0;
}
第二期
问题描述
将 2022 拆分成不同的质数的和,请问最多拆分成几个?
解析
求出2~2022的质数,把它看成一个0-1背包问题的话,每一个质数看做一个物品,质数的大小为物品质量,重质量不超出2022,最多能装多少个质数。
答案:33
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2030;
int f[N][N];
int primes[N];//2~2022的质数
int n;
int cnt = 1;//质数的个数
bool isprime(int n) {
for (int i = 2; i <= n / i; i++)
if (n % i == 0)
return false;
return true;
}//判断是否为质数(素数)
int main() {
for (int i = 2; i <= 2022; i++)
if (isprime(i))
primes[cnt++] = i;
for (int i = 1; i < cnt; i++)
for (int j = 2; j <= 2022; j++) {
if (primes[i] > j)
f[i][j] = f[i - 1][j];
else
f[i][j] = max(f[i - 1][j], f[i - 1][j - primes[i]] + 1);
}
cout << f[cnt - 1][2022] << endl;
return 0;
}
总结
多练习进制问题、日期问题(围绕2022~2023)、数学问题、矩阵问题、拆分问题,
熟练掌握枚举、动态规划、0-1背包问题等等。
👍+✏️+⭐️是对博主最大的鼓励与支持!!!