本文新增代码分析
2024/2/5 周一
日期统计——2023年C / C++B组试题A
【参考代码】
来自罗老师的代码,自己真的想不出来应该怎么写
#include <bits/stdc++.h>
using namespace std;
int num[110];
int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int main() {
for (int i=0;i<100;i++) cin >>num[i]; //输入100个数
int ans = 0;
for (int i = 1; i <= 12; i++) {
for (int j = 1; j <= days[i]; j++) {
string str = "2023"; //年
if (i < 10)
str += "0";
str += to_string(i); //+月
if (j < 10)
str += "0";
str += to_string(j); //+日
int k = 0;
for (int v = 0; v < 100 && k < 8; v++) //在100个数中找这个日期
if (num[v] == str[k] - '0')
k++;
if (k >= 8) ans++;
}
}
cout << ans << endl;
return 0;
}
【代码分析】
按照题目的说法,要在这100个数(不能变更100个数的顺序)里面找出2023的日期,并且输出符合结果数。那就枚举2023年的全部日期(一共365天),再和这100个数比较,若日期8位全部相等,则结果变量+1。
1. 枚举2023年的全部日期
用一个大小为13的数组,用于存放每月的日期数,数组下标 = 月份
int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
用两个for循环,来枚举2023年的每月每日。days【i】表示当前第 i 月份有多少天
for (int i = 1; i <= 12; i++)
for (int j = 1; j <= days[i]; j++)
2. 如何表示日期呢
首先想到用数组,只要存放8个数字就可以。但是这种存储方案方不方便呢?题目需要在月份天数小于10的时候,在个位数前面加0。下标为4和6的地方都要加0,而且还要插入单个数字。
那有没有更加简便的办法:
当然有,利用C++的STL容器,vector,string
vector容器拥有特殊的成员函数:(你定义的vector标识符).push_back(需要添加的数字)
而string类的用法就和上面的代码一样了
3. string类表示日期和判断结果
首先可以确定的是日期中2023这4个数字是不会变的
string str = "2023"; //年
string类可以理解成一个动态的字符数组,循环时可以当成数组来用,动态开辟内存空间(C语言的malloc函数)
题目需要在月份天数小于10的时候,在个位数前面加0。
str += "0";在str的末尾加上“0”字符,避免了要找数组下标插入的尴尬情况
to_string()函数是string类的成员函数,作用是将数字转成字符数字存储在string类中
if (i < 10)
str += "0";
str += to_string(i); //+月
if (j < 10)
str += "0";
str += to_string(j); //+日
k<8写在循环里面了,当然,你也可以写个if包着for循环
int k = 0;
for (int v = 0; v < 100 && k < 8; v++) //在100个数中找这个日期
if (num[v] == str[k] - '0')
k++;
if (k >= 8) ans++;
星期几
【参考代码】
一周的星期几7天一循环,所以不管是加多少天,最后结果和7取模就行。⚠️要注意当加起来的天数为7的倍数时,除以7刚好可以整除,取模后的结果为0,需要用if语句判断星期天的情况。
#include <iostream>
using namespace std;
int main()
{
// 请在此输入您的代码
int w, n;
cin>> w>>n;
int res = (w+n)%7;
if(res == 0)
cout << 7;
else
cout << res;
return 0;
}
妮妮的翻转游戏
【参考代码】
!表示取反
#include <iostream>
using namespace std;
int main()
{
// 请在此输入您的代码
int n;
cin>>n;
cout<<!n;
return 0;
}
01串的熵——2023年C / C++B组试题B
【参考代码】
来自罗老师的代码,自己真的想不出来应该怎么写
#include<bits/stdc++.h>
using namespace std;
int N = 23333333;
double ans = 11625907.5798;
double eps=1e-4; //误差
int main(){
for(int v=0; v <= N/2; v++)
{ //v是0的个数,0比1少
int u=N-v; //u是1的个数,v是0的个数
double res= -1.0* u * u / N*log2(1.0*u/N) - 1.0* v * v / N*log2(1.0*v/N);
if(fabs(res-ans) < eps)
{ //res和ans相减小于误差,认为相等
cout<<v; //找到了v
break;
}
}
return 0;
}
【代码分析】
先看题目,求0的个数。由于现在0和1的数量都不知道,所以我们列方程。假设x为0的个数,则23333333-x为1的个数。
再看公式定义
下方所例子化简可得:
对应代码:乘1.0是保留小数,为了变量转换为浮点型
double res= -1.0* u * u / N*log2(1.0*u/N) - 1.0* v * v / N*log2(1.0*v/N);
题目已经给出信息熵,让我们反推0的个数。采用枚举法,还需要设置一个误差为0.001。
记得写头文件<math.h>
for(int v=0; v <= N/2; v++)
{ //v是0的个数,0比1少
int u=N-v; //u是1的个数,v是0的个数
double res= -1.0* u * u / N*log2(1.0*u/N) - 1.0* v * v / N*log2(1.0*v/N);
if(fabs(res-ans) < eps)
{ //res和ans相减小于误差,认为相等
cout<<v; //找到了v
break;
}
}
fabs()是用于计算浮点数的绝对值
2024/2/6 周二
列名
【参考代码】
//使用excel即可
cout << "BYT";
信号覆盖
【参考代码】
#include <iostream>
using namespace std;
struct node
{
int heng;
int zong;
}a[105];
int pos[101][101]; //用于坐标去重
//数学公式圆的方程:(x-a)^2+(y-b)^2=r*r
int main()
{
// 请在此输入您的代码
int w, h, n, r;
cin>>w>>h>>n>>r;
int cnt = 0;
for(int i=0;i<n;i++)
{
cin>>a[i].heng>>a[i].zong;
}
for(int i=0;i<n;i++)
{
for(int j=0; j<=w; j++)
{
for(int k=0; k<=h; k++)
{
if((a[i].heng-j)*(a[i].heng-j)+(a[i].zong-k)*(a[i].zong-k)<=r*r)
{
if(pos[j][k] != 1) //去重
pos[j][k] = 1;
}
}
}
}
for(int i=0;i<=w;i++) //算坐标总数
for(int j=0; j<=h; j++)
if(pos[i][j] == 1)
cnt++;
cout << cnt;
return 0;
}
【代码分析】
圆上有一点(x,y)圆的方程为(x-a)²+(y-b)²=R²
本题就是围绕这个公式展开的
因为需要接收点的坐标,坐标包括横坐标和纵坐标,所以设置了一个struct结构体变量
struct node
{
int abscissa; //横坐标
int ordinate; //纵坐标
}a[105];
这样子就不用设置两个一维数组了
for(int i=0;i<n;i++)
{
for(int j=0; j<=w; j++)
{
for(int k=0; k<=h; k++)
{
if((a[i].abscissa-j)*(a[i].abscissa-j)+(a[i].ordinate-k)*(a[i].ordinate-k)<=r*r)
{
if(pos[j][k] != 1) //去重
pos[j][k] = 1;
}
}
}
}
for(int i=0;i<n;i++)第一层循环为了遍历输入的点
for(int j=0; j<=w; j++)第二层循环
for(int k=0; k<=h; k++)第三层循环为了实现题目要求。从0~w循环w+1次,从0~h循环h+1次
只要该点在圆上,或者是圆内,就可以被标记为有信号。定义一个100*100的二维数组,用于标记该点是否有信号,同时去掉重复遍历的点(符合圆方程条件,而且pos数组未被变成1,就把该点置1)。
int pos[101][101]; //用于坐标去重
if((a[i].abscissa-j)*(a[i].abscissa-j)+(a[i].ordinate-k)*(a[i].ordinate-k)<=r*r)
{
if(pos[j][k] != 1) //去重
pos[j][k] = 1;
}
最后遍历这个pos二维数组,若该点为1,则结果+1
for(int i=0;i<=w;i++) //算坐标总数
for(int j=0; j<=h; j++)
if(pos[i][j] == 1)
cnt++;
cout << cnt;
统计数字
⚠️题目出错:每个数均不超过1.5 * 10^9
【参考代码】
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 每个数均不超过1.5*10^9
int n;
cin>>n;
int a[200001] = {0};
for(int i=1;i<=n;i++)
{
cin >> a[i];
}
sort(a+1,a+n+1);
int cnt = 1;
for(int i=1;i<=n;i++)
{
if(a[i] == a[i+1])
{
cnt++;
}
else
{
cout << a[i] << " " << cnt << endl;
cnt = 1;
}
}
return 0;
}
【代码分析】
使用一个一维数组接收输入的数字。开头第一个数字是输入数字的个数,用于for循环多组输入。
当然,为了保证结果准确性,需要数组置0
int n;
cin>>n;
int a[200001] = {0};
for(int i=1;i<=n;i++)
{
cin >> a[i];
}
题目需要升序输出数字,并且记录每个数字的个数。接下来进行sort排序,为了方便之后的计数操作。由于数组存放元素从下标1开始,所以sort函数也需要从1开始(a的地址为a[0])
sort(a+1,a+n+1);
计数操作:cnt计数器先置1。因为数量最少情况是1个数字,而不是0个。需要判断的是如果 i 所指向的数字和 i+1 比较,不相等就证明这个数字已经计数完毕了,应该计数下一种数字了。计数完毕输出cnt,并把cnt置1(重置cnt的值,防止cnt越来越大,而且输出不符合要求)
int cnt = 1;
for(int i=1;i<=n;i++)
{
if(a[i] == a[i+1])
{
cnt++;
}
else
{
cout << a[i] << " " << cnt << endl;
cnt = 1;
}
}
奇怪的数列
【参考代码】
#include <iostream>
#include <string>
using namespace std;
int main()
{
// 请在此输入您的代码
string s1;
string s2 = "";
int n;
cin>>s1;
cin>>n;
for(int i=0;i<n;i++)
{
int cnt = 1;
for(int j=0;j<s1.size();j++)
{
if(s1[j] == s1[j+1])
cnt++;
else
{
s2 += to_string(cnt);
s2 += s1[j];
cnt = 1;
}
}
s1 = s2;
s2 = "";
}
cout << s1;
return 0;
}
【代码分析】
这道题非常有意思,一开始我还get不到它的意思
知道就好办了,这道题和上道题统计数字一样,需要一个cnt计数器计数。由于数字太大(不超过100位),需要使用string类。首先是定义变量,定义两个字符串,还有分裂次数n(用于循环)
string s1;
string s2 = "";
int n;
cin>>s1;
cin>>n;
写一个循环,用于遍历分裂次数。定义cnt = 1。
计数操作:cnt计数器先置1。因为数量最少情况是1个数字,而不是0个。需要判断的是如果 j 所指向的数字和 j+1 比较,不相等就证明这个数字已经计数完毕了,应该计数下一种数字了。计数完毕输出cnt,并把cnt置1(重置cnt的值,防止cnt越来越大,而且输出不符合要求)
to_string(cnt)作用是将cnt从int型转成string类型,+=是在s2末尾加上一个字符。
最后要记得cnt = 1,将s2赋值给s1,s2变成空串
for(int i=0;i<n;i++)
{
int cnt = 1;
for(int j=0;j<s1.size();j++)
{
if(s1[j] == s1[j+1])
cnt++;
else
{
s2 += to_string(cnt);
s2 += s1[j];
cnt = 1;
}
}
s1 = s2;
s2 = "";
}
2024/2/7 周三
与或异或
【参考代码】
#include <bits/stdc++.h>
using namespace std;
int a[6][6];
int res = 0;
void dfs(int i, int j, int op)
{
if(i>5 || j > 5-i+1)
return;
if(op == 1)
a[i][j] = a[i-1][j] & a[i-1][j+1];
else if(op == 2)
a[i][j] = a[i-1][j] | a[i-1][j+1];
else if(op == 3)
a[i][j] = a[i-1][j] ^ a[i-1][j+1];
if(i == 5 && j == 1 && a[5][1] == 1)
{
res++;
return;
}
if(j == 5-i+1)
{
i++;
j=1;
}
else
j++;
dfs(i, j, 1);
dfs(i, j, 2);
dfs(i, j, 3);
}
int main()
{
// 30528
a[1][1] = 1, a[1][2] = 0, a[1][3] = 1, a[1][4] = 0, a[1][5] = 1;
dfs(2, 1, 1);
dfs(2, 1, 2);
dfs(2, 1, 3);
cout << res;
return 0;
}
【代码分析】
简单题不简单!dfs也算暴力的一种,dfs每年都考,需要具体问题具体分析。
这是一道填空题,填结果就行,因为跑代码可能会超时
这张图,把它变成二维数组,一个5*5的矩阵(放在全局变量里面,res变量也放,方便取用)。设置好首行元素10101,接下来就是dfs函数发挥作用了
传入矩阵的行,列,op = 当前所做的操作(& | ^)
void dfs(int i, int j, int op)
结束条件:当行数大于5,或者列数大于5-i+1(从1,1开始),5-i-1(从0,0开始)
(行数和列数的关系,下一行元素与上一行元素的对应关系)
if(i>5 || j > 5-i+1)
return;
还有结果条件,需要(& | ^)操作后才能得出。
如果行数=5,列数=1,数组元素=1,满足题目结果条件,res++。
if(op == 1)
a[i][j] = a[i-1][j] & a[i-1][j+1];
else if(op == 2)
a[i][j] = a[i-1][j] | a[i-1][j+1];
else if(op == 3)
a[i][j] = a[i-1][j] ^ a[i-1][j+1];
if(i == 5 && j == 1 && a[5][1] == 1)
{
res++;
return;
}
dfs函数不能自己做二维数组遍历操作。到一行的末尾时,自动转到下一行第一个空
if(j == 5-i+1)
{
i++;
j=1;
}
else
j++;
递归函数,三个状态都要考虑。点i,j需要选择(& | ^)得出一个值(0, 1)
dfs(i, j, 1);
dfs(i, j, 2);
dfs(i, j, 3);
最后的递归入口也不要忘了
dfs(2, 1, 1);
dfs(2, 1, 2);
dfs(2, 1, 3);
阶乘求和
【参考代码】
#include <iostream>
using namespace std;
int main()
{
// 请在此输入您的代码
cout <<420940313 << endl;
return 0;
}
【代码分析】
420940313是如何得出来的?
要算到202320232023的阶乘是不可能的。容易发现100! 的末尾已经有9个0了,对阶乘的和S的末9位不再有影响。
所以只需要算到100的阶乘就够了。就考核这一点。
因为数字很大,可以一边计算一边对10^9取模,不然结果就为long long类型的最大值
#include <iostream>
using namespace std;
int mod=1000000000;
int main()
{
// 请在此输入您的代码
long long ans=0,temp=1; //warn:int会溢出
for(int i=1;i<=100;i++)
{
temp = temp * i % mod;
ans = (ans + temp) % mod;
cout<<ans<<endl;
}
return 0;
}
temp = temp * i % mod;是计算阶乘的值,ans用来求和。因为数字很大,可以一边计算一边对10^9取模,不然结果就为long long类型的最大值
2024/2/8 周四
饮料换购
【参考代码】
#include <iostream>
using namespace std;
int main()
{
// 请在此输入您的代码
int n;
cin >> n;
int sum = n;
int a, b;
b = n % 3;
a = n / 3;
sum = sum + a;
while(a+b>=3)
{
a = a+b;
b = a % 3;
a = a / 3;
sum += a;
}
cout << sum;
return 0;
}
【代码分析】
代码一次过,爽!
首先就是输入饮料数,先算上sum中。
int n;
cin >> n;
int sum = n;
喝完了这n瓶饮料,是时候兑换瓶盖了
定义a,b。a是n个瓶盖能兑换多少饮料,b是换完之后还剩多少瓶盖
int a, b;
b = n % 3; //换完之后还剩多少瓶盖
a = n / 3; //兑换多少饮料
喝饮料:sum = sum + a;
经过这番操作后,就可以开始循环了。循环条件a+b>=3,证明瓶盖数量还能换饮料。
把瓶盖集中在a中:a=a+b,先看看有没有多的瓶盖(除不尽),再去换饮料,顺序反过来好像也行。最后输出结果
while(a+b>=3)
{
a = a+b;
b = a % 3;
a = a / 3;
sum += a;
}
cout << sum;
打印大X
【参考代码】
#include <iostream>
using namespace std;
int main()
{
int m, n;
cin >> m >> n;
int width = m +n-1; //规律得出
for (int i = 0; i < n; i++) //总行数
{
for (int j = 0; j < width; j++)
{
if (j >= i && j < i + m) //判断输出*号的情况
cout << "*";
else if (j >= width - i - m && j < width - i) { //判断输出*号的情况
cout << "*";
}
else
{
cout << ".";
}
}
cout << endl;
}
return 0;
}
【代码分析】
这题只需要我们输入两个数据(笔画的宽度和字的高度),因此我们可以发现缺少了字的宽度。
数据与图形的关系是:
1、字的宽度 = 笔画的宽度 + 字的高度 - 1
2、X的每一层笔画到边缘都是有规律的
int m, n;
cin >> m >> n;
int width = m +n-1; //规律得出
然后就是打印,把这个图案看成二维数组。行数列数都与*号有关系。只需要判断*号情况就行,因为宽度已经确定了,填完了*号剩下的位置就是填.
行数是用于确定 . 的数量,还有*的起始位置。
for (int i = 0; i < n; i++) //总行数
{
for (int j = 0; j < width; j++)
{
if (j >= i && j < i + m) //判断输出*号的情况
cout << "*";
else if (j >= width - i - m && j < width - i) { //判断输出*号的情况
cout << "*";
}
else
{
cout << ".";
}
}
cout << endl;
}
凯撒加密
【参考代码】
WA了一发,没看到x变成a,y变成b。。。。
#include <iostream>
#include <string>
using namespace std;
int main()
{
// 请在此输入您的代码
string s1, s2;
cin >> s1;
for(int i=0;i<s1.size();i++)
{
s1[i] += 3;
if(s1[i] > 'z')
s1[i] -= 26;
}
cout << s1;
return 0;
}
2024/2/9 周五
2023
【参考代码】
#include <iostream>
#include <string>
using namespace std;
int main()
{
//print(85959030)
cout <<85959030;
return 0;
}
【代码分析】
#include <iostream>
#include <string>
using namespace std;
string s = "2023";
int check(int n)
{
string s1;
s1 += to_string(n); //要用+=
int pos = 0;
for(int i=0;i<s1.size();i++)
{
if(s1[i] == s[pos])
pos++;
if(pos == 4)
return 1; //包含2023
}
return 0; //完全不包含2023
}
int main()
{
// 请在此输入您的代码
int res = 0;
for(int i=12345678; i<=98765432; i++)
{
if(!check(i)) //记录完全不包含2023的情况
res++;
}
cout <<res;
return 0;
}
代码参考罗勇军老师的,写得比较简洁
题目要求找出不完全是2023的情况数,由于数字有8位,我们依然使用string类型。
本题核心点就是判断8位数字里面有没有2023
定义一个“标准答案”
string s = "2023";
构造一个函数check,建立一个string变量存放输入的数。循环遍历整个字符串。pos记录是否等于2023的顺序,如果相等,数量为4,返回1,否则返回0
int check(int n)
{
string s1;
s1 += to_string(n); //要用+=
int pos = 0;
for(int i=0;i<s1.size();i++)
{
if(s1[i] == s[pos])
pos++;
if(pos == 4)
return 1; //包含2023
}
return 0; //完全不包含2023
}
定义res变量,用于存放符合情况的数量。for循环枚举每一个数字,if判断是否存在不包含2023的情况,不包含res++。输出res
int res = 0;
for(int i=12345678; i<=98765432; i++)
{
if(!check(i)) //记录完全不包含2023的情况
res++;
}
cout <<res;
积木
【参考代码】
75%的样例
#include <iostream>
using namespace std;
int a[1000][1000];
int main()
{
// 请在此输入您的代码
int n, m;
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>a[i][j];
int h, cnt = 0;
cin>>h;
for(int k=1;k<=h;k++)
{
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
if(a[i][j] >= k)
cnt++;
}
cout << cnt << endl;
}
return 0;
}
【代码分析】
75%心满意足了
城堡图是一个矩阵,那就用二维数组来接收数据,并且遍历每个数字
n行m列,输入进1000*1000的数组中
int n, m;
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>a[i][j];
最后还有一个水的高度
int h, cnt = 0;
cin>>h;
三层循环暴力,最后一个案例超时😂。k对应水的高度。这里的cnt就不用重置了,因为泡了水的积木就算泡了。
for(int k=1;k<=h;k++)
{
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
if(a[i][j] >= k)
cnt++;
}
cout << cnt << endl;
}