第十七周代码(跟着罗勇军老师刷题)

本文新增代码分析

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;
  }
  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值