首先声明!!!


  • 1、此知识点为课堂总结内容;
  • 2、如有使用或转载请注明出处;
  • 3、如有不足,欢迎批评指正;

* C++导言.软件

1. 导言须知

  • 课程方向:

    • 知识点主要面向小学**5年级+**的少儿编程 C++ 课程,适用于信奥赛选拔;

    • 特别注意①:C++ 课程 只有启蒙这一套是 小学阶段适合,后续全部中学;

    • 特别注意②:虽然是 C++ 编程课程,但和 C++ 几乎没有关系,如何理解?

      • 很多学习者在学习时,找那种职业成人的C++课程,咨询学了是否可以打比赛?
      • 你学习的是信息学奥林匹克竞赛之启蒙阶段,只不过用了官方推荐的 C++ 环境罢了;
      • 如果官方推荐了别的编程环境,学习的内容区别不大,用不到 C++ 自身的特性;
      • 比如 C++ 可以做嵌入式开发、客户端服务端开发、音频视频开发、大型游戏开发等;
      • 上面所说的东西,都不会学到,根本用不到 C++ 的各种能力。
      • 而我们学习的是通过 C++ 编程环境,去解决计算机思维运算下的数理逻辑题罢了。
      • 总结:我们的目的是 答题,不是做 开发。说白了就是 数学应用题考试
  • 知识点规划:

    • 启蒙阶段(本套):

      • 语法基础、分支、循环、数组、函数、字符串、结构体;
      • 其中函数的递归详解或深入、数组中的排序算法、字符串的进制、编码、位运算不包含;
    • 后续阶段(非本套):

      • 过渡篇:进制转换、编码、位运算、暴力枚举、高精度等;
      • 排序篇:冒泡、插入、选择、快速、归并、sort 排序等;
      • 算法篇:递归、递推、模拟、分治、二分、贪心、倍增等;
      • 搜索篇:深度优先DFS、广度优先BFS、回溯、剪枝等;
      • 数据结构篇:链表、队列、栈、二叉树、并查集等;
      • 图论篇:图的存储、最小生成树、最短路、二分图等;
      • 动态规划篇:背包DP、线性DP、树形DP、区间DP、记忆化搜索等;
      • 数论篇:筛质数、分解质因数、快速幂、组合数等;
    • 详情参考:

      • 上面只列出了大概知识点,详情参考:学习路线 - OI Wiki (oi-wiki.org)
      • 一般培训机构,大概率只会做到 CSP-J/S 入门级的比赛,难度尚可;
      • 更高级的比如 NOIP同等更高,NOIP 只能教一点,再多就能力不够了;

2. 软件使用

  • 下载安装:

    • 官网:DevC++
    • 安装后推荐皮肤和设置,并根据自己的喜好调整:
  • 第一个C++程序:

    1
    2
    3
    4
    5
    6
    7
    #include <iostream>
    using namespace std;
    int main()
    {
    cout << "Hello World!";
    return 0;
    }
  • 程序详解:

    • 真正需要理解的就一行:cout << “Hello World!”;

      • cout 表示 输出,直白点:运行程序后在窗口显示后面双引号里面的内容;
      • << 表示 插入运算符,语法表示:在 cout 和 后面输出内容,需要用这个+空格隔开;
      • ; 表示语句结束,一条输出语句写完后,需要通过分号表示结束;
    • 其余的代码部分,全员基本格式,无须理解,做成模板自动即可;

      • 虽说无须理解,但还是象征性的解释一下:
        • include:头文件,导入外部库;iostream 表示支持输入输出;
        • using…:命名空间,防止冲突;
        • int main:主函数,程序会从这里执行;
        • return 0:返回0,表示程序在这里结束;

一、变量存储.注释

1. 变量声明

  • 什么是变量?变量就是一个可以存储东西的存储罐,具体特性如下:

    • 每次存储罐里只能存一次东西,也就是说再次存储时,上一次存储的东西会被替换掉;
    • 声明变量就好比你拿出了一个存储罐,实际上是在内存里开辟了一个空间;
    • 不管是现实中的存储罐还是内存开一个空间,多了防止混淆就需要贴个标签,命个名
    • 我们来通过一张图来理解一下,这个声明概念:
  • 那么,如何通过编程来实现变量的声明呢?

    1
    2
    3
    4
    5
    6
    7
    // 声明一个变量a,类型是整数
    int a;
    // 注意1:= 不是等于的意思,是赋值的意思
    // 注意2:阅读代码从右边向左边阅读,意为:将数字123赋值给变量a
    a = 123;
    // 输出变量a
    cout << a;

2. 命名规则

  • a=123 的例子中,a是变量名,但变量的名字有什么要求?需要注意哪些规则?能乱起吗?

    • 变量名只能是:a-z、A-Z、0-9以及下划线组成;

    • 变量名第一位不可以是数字;

    • 变量名不可以是 C++ 关键字:比如 int main 之类的;

    • 变量需要定义后才可以使用:int a; a = 123;

    • 变量名区分大小写,A和a不同;

      • abc(合法)、a123(合法)、_xyz(合法)、123a(不合法)
  • 为了让变量名更加的有可读性,比如:你的名字:

    • 第一种经典命名方式,驼峰式:yourName,或YourName,我比较喜欢前者;
    • 第二种经典命名方式:蛇形式:your_name,用的也比较多;
    1
    2
    3
    4
    5
    // 声明一个字符串
    string yourName;
    // 字符串类型,需要双引号包含
    yourName = "你的名字";
    cout << yourName;

二、四则运算.输入

1. 加减乘除

虽说叫做四则运算,但其实运算符往往不止加减乘除这四种:

运算 符号 格式
+ 1 + 1
- 2 - 1
* 3 * 4
/ 6 / 2
取余 % 10 % 7
累加 ++ i++
累减 i–
  • 在编程语法中乘法 x* 代替,除法 ÷/ 代替。

    1
    2
    3
    4
    5
    6
    // endl 表示换行 Ctrl + D复制一行
    cout << 1 + 1 << endl;
    cout << 3 - 2 << endl;
    cout << 4 * 5 << endl;
    cout << 9 / 7 << endl;
    cout << 7 % 3 << endl;

2. cin 输入

如果想让用户在键盘输入内容,可以使用 cin 关键字;

1
2
3
4
5
6
7
// 同时声明两个整型变量
int a,b;
// 输入两个数,并赋值给a和b
// 注意:同时输入a和b时,中间需要用空格键隔开
cin >> a >> b;
// 打印a+b的和
cout << a + b;

三、学会使用OJ答题

1. OJ系统

所谓的OJ系统,即:用于竞赛编程中,海量刷题的工具系统;

  • 这里推荐几个刷题的OJ平台:

  • 这里摘一段博宜答题须知:

    • 程序题和平时纸上做的数学题不同,它包括了很多部分:题目、输入输出要求、输入输出样例、数据范围和时间限制和样例解释(说明)这几大部分,这几个部分都非常重要,每一部分都要看!!!
    • 提交代码前必须要完成的三步!!!
      1、在编译器里写完程序后要先编译通过(Devc++是弹出黑框框);
      2、把题目是输入样例复制进去,用程序跑一遍,和题目给的输出样例对上了;
      3、再检查一下程序,看有没有其他的一些情况没有考虑到的,检查无误后方可提交;
    • 不要有多余的输入输出!!!输出格式和题目要求要完全一致;
    • 样例只是其中一个测试点,并非全部,后台会有很多其他数据测试你这个程序的。
    • 比如:要求输入1和2,输出和3;
      • 你不能 输入1和2,输出3
      • 你应该 输入1和2,输出1+2
      • 因为其它测试点可能是4和5,要求输出9

2. 使用洛谷

  • 在首页直接输入题号:B2002,可直接进入题目页面;题号不区分大小写;

  • 或者在右侧,有一个 题单 >> 【入门1】顺序结构 >> 题目列表 >> B2002 Hello,World!

    • 开始读题

    • 在 Dev-C++编译器上 编写代码,并 编译 + 运行 结果;

    • 将代码复制,回到答题页,选择 提交答案

    • 选择 C++ 标准 ,最近的就行,提交测评

    • 出现全部都是 绿色AC,表示正确,如果答案不对:则有可能会出现以下错误:WA(答案错误)、RE(越界问题)、TLE(时间超限)、MLE(内存超限)、CE(编译错误);

      1
      2
      3
      4
      5
      6
      7
      #include <iostream>
      using namespace std;

      int main() {
      cout << "Hello,World!";
      return 0;
      }

四、数据类型.转换

1. 数据类型

类型名 类型声明 解释说明
整型 int -2147483648 到 2147483647 的范围的整数
长整形 long long 比上面大很多很多,这里装不下,具体搜索
字符型 char 赋值只能是一个字符,比如 ‘A’,必须用单引号,双表示字符串不是字符
浮点型 float 也叫实型或小数型,38位以内的小数,只保留6为有效数字
  • 其实还有很多类型,暂时先理解这几个,后续用到的再单独讲解;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 字符型,切记是单引号
    char ch = 'a';
    // 长整形
    long long b = 100000000000000000;
    // 浮点型,只能六位
    float c = 3.1415926;
    cout << b << endl;
    cout << ch << endl;
    cout << c << endl;
    return 0;

2. 类型转换

  • 这里我们只探讨整数和小数之间的转换,其它的放到以后;

  • 类型转换分为两种:隐式转换和强制转换;

    1
    2
    3
    4
    5
    6
    7
    8
    // 两边都是整数,结果也是整数:1
    cout << 8 / 5 << endl;
    // 两边有一边是浮点,结果是浮点:1.6
    cout << 8.0 / 5 << endl;
    // 强制转换浮点,float()是转换函数,将整数转换成浮点数,8叫做参数
    cout << float(8) / 5 << endl;
    // 强制转换整型
    cout << int(3.14 * 5.25) << endl;

3. 保留小数点

  • 首先要引入相关库:

  • 其次使用 setprecision(n) 函数 来设置保留小数点的位数;

    1
    2
    3
    // 输出保留的小数点
    // fixed可以在缺少的位数补零,这样就精确保证2位
    cout << fixed << setprecision(2) << 3.1415926;

习题一:1029.倒序输出一个四位整数

1029.倒序输出一个四位整数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 输入一个四位整数
int a, qian, bai, shi, ge;
cin >> a;

// 拆位
qian = a / 1000;
bai = a / 100 % 10;
shi = a / 10 % 10;
ge = a % 10;

// 倒序
// 这里有一个问题:如果 cout << ge << shi << bai << qian;
// 会有一个测试点无法通过;因为第二个样例中,1600,会去掉零,所以需要通过加整,而不是连整;
cout << ge * 1000 + shi * 100 + bai * 10 + qian;

五、输入输出.格式化

1. 输出

对于格式化输出,本节课将学习两个函数 scanf()printf() 来取代之前的cin和cout方法;

  • 假如,我们要输出一组运算等式:1 + 2 = 3,而1是变量a,2是变量b,3是a+b的运算结果;
1
2
3
4
5
6
7
8
9
int a = 1, b = 2;
// cout
cout << a << " + " << b << " = " << a + b;

// 换行
cout << endl;

// printf
printf("%d + %d = %d", a, b, a + b);
  • printf() 函数极其方便的原因,它可以先按照自己的输出格式,先写出来,不需要头脑翻译;

  • 然后把需要替换成变量的部分,用 %d 来表示即可,而后续对应的位置换成变量或表达式运算即可;

  • %d 这里表示占位的内容 是 整型,也有各种其它的占位符,这里罗列出常用的几个:

    占位控制符 说明
    %d 整数
    %f 浮点数(float)
    %c 字符
    %s 字符串
  • 最终的语法结构如下:

    1
    printf(格式控制符, 占位替换列表)

2. 输入

  • 从上面例子中,我们使用 scanf() 函数来控制输入:

    1
    2
    3
    4
    5
    6
    int a, b;
    // scanf
    scanf("%d %d", &a, &b);

    // printf
    printf("%d + %d = %d", a, b, a + b);
  • 先了解下输入参数的格式需求:

    1
    scanf(格式控制符, 地址集)
  • 暂时没有办法解释这个地址是什么意思?举个并不恰当但好理解的说明:

    • 快递员给张三寄快递,直接送到张三手上;这是 cin
    • 快递员给张三家地址寄快递,不管是凤巢、门卫代收还是家人收件,最终会交给张三;这是 scanf()
    • 所以,a表示张三,&a表示张三家的地址;而sacanf() 参数要求是地址,故在变量前加&;
    • 而这个知识点叫做 引用,是比较靠后的知识点,暂时不用理解;
    • 后续,自然也会有 变量本身就是引用,反而参数不需要 & 符号;

3. 格式化

  • 在使用 printf() 时,我们需要输出各种特殊字符,比如百分号,换行,以及场宽精度问题等:

    符号 说明和示例
    \\ 输出一个斜杠,printf(“\\”);
    \n 换行,printf(“\n”);
    %% 百分号,printf(“%”);
    数字 场宽,printf(“%3d%5d”);
    小数 精度,printf(“%.2f”, 3.1415926);
    1
    2
    3
    4
    5
    6
    7
    8
    // 格式化
    printf("%%\\\n");
    // 场宽
    printf("%3d%5d\n", a, b);
    // 精度
    printf("%.2f\n", 3.1415926);
    // 场宽+精度
    printf("%8.2f", 3.1415926);

习题二:P5705.数字反转

P5705.数字反转

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 输入一个浮点数,只有一位小数
float a;
int b, qian, bai, shi, ge;
scanf("%f", &a);

// 各个位进位
b = a * 10;

// 拆位
qian = b / 1000;
bai = b / 100 % 10;
shi = b / 10 % 10;
ge = b % 10;

// 合并输出
printf("%d.%d%d%d", ge, shi, bai, qian);

六、运算符表达式.布尔

1. 关系运算符

布尔类型的值,是专门用于各种表达式的判断,只存在两种值:

1
2
3
4
5
6
7
8
// 布尔类型,只有两种值:true和false
// true 表示成立, false 表示不成立
// true 输出为1, false 输出为 0
bool a = true;
bool b = false;

cout << a << endl;
cout << b << endl;
  • 除了直接赋值为 true 或 false,也可以通过关系运算符来返回布尔值的数据:
名称 关系运算符 表达式返回值
大于 > 成立返回 true(6 > 5),否则为 false(6 > 7)
小于 < 成立返回 true(6 < 8),否则为 false(6 < 5)
等于 == 成立返回 true(6 == 6),否则为 false(6 == 7)
大于等于 >= 成立返回 true(7 >= 6),否则为 false(7 >= 8)
小于等于 <= 成立返回 true(6 <= 6),否则为 false(6 <= 5)
不等于 != 成立返回 true(6 != 5),否则为 false(6 != 6)
1
2
3
4
5
6
7
8
9
// 返回 true(1) 和 false(0)
cout << (6 > 5) << endl;
cout << (6 > 7) << endl;
cout << (6 < 7) << endl;
cout << (6 == 6) << endl;
cout << (6 == 7) << endl;
cout << (6 >= 4) << endl;
cout << (6 != 6) << endl;
cout << (6 != 5) << endl;

2. 逻辑运算符

除了关系运算符之外,还有一种叫做逻辑运算符,具体如下:

名称 逻辑运算符 表达式返回值
&& x && y 表示and,x 和 y 同时为 true 时,返回 true,否则返回 false
|| x || y 表示or, x 和 y 只要其中之一为 true,返回 true,否则返回 false
! !x 表示not, x 为 true 时,结果为 false,否则为 true
1
2
3
4
5
6
7
# 逻辑运算符
cout << (5 > 6 && 5 > 4) << endl;
cout << (7 > 6 && 5 > 4) << endl;
cout << (5 > 6 || 5 > 4) << endl;
cout << (5 > 6 || 5 < 4) << endl;
cout << !(5 > 6) << endl;
cout << !true << endl;

习题三:B2040.判断是否为两位数

B2040.判断是否为两位数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int a;
bool flag;

// 输入
cin >> a;

// 赋值
// 条件1:a >= 10
// 条件2:a <= 99
// 整合:a >= 10 && a <= 99
// 看花眼的话,可以加个括号增加可读性
// 括号加法1:(a >= 10 && a <= 99)
// 括号加法2:((a >= 10) && (a <= 99))
flag = ((a >= 10) && (a <= 99));

// 输出
cout << flag;

* 补充:C++ 运算符优先级总表

来自 C++ 运算符优先级 - Oi-Wiki ,有修改。

运算符 描述 例子 可重载性
第一级别
:: 作用域解析符 Class::age = 2; 不可重载
第二级别
++ 后自增运算符 for (int i = 0; i < 10; i++) cout << i; 可重载
后自减运算符 for (int i = 10; i > 0; i–) cout << i; 可重载
type() type{} 强制类型转换 unsigned int a = unsigned(3.14); 可重载
() 函数调用 isdigit(‘1’) 可重载
[] 数组数据获取 array[4] = 2; 可重载
. 对象型成员调用 obj.age = 34; 不可重载
-> 指针型成员调用 ptr->age = 34; 可重载
第三级别 (从右向左结合)
++ 前自增运算符 for (i = 0; i < 10; ++i) cout << i; 可重载
前自减运算符 for (i = 10; i > 0; --i) cout << i; 可重载
+ 正号 int i = +1; 可重载
- 负号 int i = -1; 可重载
! 逻辑取反 if (!done) … 可重载
~ 按位取反 flags = ~flags; 可重载
(type) C 风格强制类型转换 int i = (int) floatNum; 可重载
* 指针取值 int data = *intPtr; 可重载
& 值取指针 int *intPtr = &data; 可重载
sizeof 返回类型内存 int size = sizeof floatNum; 不可重载
第四级别
.* 类对象成员引用 obj.*var = 24; 不可重载
->* 类指针成员引用 ptr->*var = 24; 可重载
第五级别
* 乘法 int i = 2 * 4; 可重载
/ 除法 float f = 10.0 / 3.0; 可重载
% 取余数(模运算) int rem = 4 % 3; 可重载
第六级别
+ 加法 int i = 2 + 3; 可重载
- 减法 int i = 5 - 1; 可重载
第七级别
<< 位左移 int flags = 33 << 1; 可重载
>> 位右移 int flags = 33 >> 1; 可重载
第八级别
< 小于 if (i < 42) … 可重载
<= 小于等于 if (i <= 42) … 可重载
> 大于 if (i > 42) … 可重载
>= 大于等于 if (i >= 42) … 可重载
第九级别
== 等于 if (i == 42) … 可重载
!= 不等于 if (i != 42) … 可重载
第十级别
& 位与运算 flags = flags & 42; 可重载
^ 位异或运算 flags = flags ^ 42; 可重载
| 位或运算 flags = flags | 42; 可重载
第十一级别
&& 逻辑与运算 if (conditionA && conditionB) … 可重载
|| 逻辑或运算 if (conditionA || conditionB) … 可重载
第十二级别 (从右向左结合)
? : 条件运算符 int i = a > b ? a : b; 不可重载
= 赋值 int a = b; 可重载
+= 加赋值运算 a += 3; 可重载
-= 减赋值运算 b -= 4; 可重载
*= 乘赋值运算 a *= 5; 可重载
/= 除赋值运算 a /= 2; 可重载
%= 模赋值运算 a %= 3; 可重载
<<= 位左移赋值运算 flags <<= 2; 可重载
>>= 位右移赋值运算 flags >>= 2; 可重载
&= 位与赋值运算 flags &= new_flags; 可重载
^= 位异或赋值运算 flags ^= new_flags; 可重载
|= 位或赋值运算 flags |= new_flags; 可重载
第十三级别
, 逗号分隔符 for (i = 0, j = 0; i < 10; i++, j++) … 可重载

七、分支语句.条件判断

1. if…单一条件

假设你一周七天中只有周一才能穿新衣服,那么就需要 if语句单一条件判断

条件判断流程图

  • 单一条件判断的if语句格式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 第一个花括号放在第一行末,或第二行开头均可,执行语句需要缩进一个Tab键
    if (条件表达式) {
    条件成立执行这里;
    }

    // 注意1:如果成立后的语句只有一句,可以去掉花括号,或放在第一行末
    if (条件表达式) 条件成立执行这里;

    // 或
    if (条件表达式)
    条件成立执行这里;
    1
    2
    3
    4
    5
    6
    7
    int flag;
    cin >> flag;

    // flag == 1 返回布尔值,成立的话执行缩进的第一行语句
    // 如果有缩进的第二行,且没有括号包含,则无效
    if (flag == 1)
    cout << "周一穿新衣!";

2. if…else分支

单一if语句比较高冷,如果未满足条件,ta就不理你了;而else分支则可爱许多;

分支判断流程图

  • else分支条件判断的if语句格式如下:
1
2
3
4
5
if (条件表达式) {
条件成立执行这里;
} else {
条件不成立执行这里;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 单行语句
if (flag == 1)
cout << "周一穿新衣!";
else
cout << "没有新衣穿!";

// 多行语句
if (flag == 1) {
cout << "嘿嘿!";
cout << "周一穿新衣!";
} else {
cout << "哎!";
cout << "没有新衣穿!";
}

习题四:1632.需要几辆车

1632.需要几辆车

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int n, a;
cin >> n;

// 判断人数是否刚好30人
if (n % 30 == 0)
a = n / 30;
else
// 31人 = 1 + 1 = 2辆
// 65人 = 2 + 1 = 3辆
a = n / 30 + 1;

// 输出
cout << a;
return 0;

习题五:1045.能否构成三角形

1045.能否构成三角形

* 代码详解

1
2
3
4
5
6
7
8
9
int a, b, c;
cin >> a >> b >> c;

// 判断三角形
if (a + b > c && a + c > b && b + c > a)
cout << "Yes";
else
cout << "No";
return 0;

习题六:1718. 闯关大冒险?

1718. 闯关大冒险?

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;

int main() {
int n, a, b, c, d, maxn;
cin >> n;

// 拆位
a = n / 1000;
b = n / 100 % 10;
c = n / 10 % 10;
d = n % 10;

// 求最大值,初始值比规定的小即可,四位数的话最小是0,-1即可
maxn = -1;

// a
if (a > maxn)
maxn = a;
//b
if (b > maxn)
maxn = b;
//c
if (c > maxn)
maxn = c;
//d
if (d > maxn)
maxn = d;

// 输出
cout << maxn;

return 0;
}

八、多重判断.嵌套判断

1. else if…分支

很多时候的分支判断,可能不止一条或两条,此时就需要使用 else if 多重分支结构

1
2
3
4
5
6
7
8
9
10
11
if (表达式成立) {
执行语句;
} else if (表达式成立) {
执行语句;
} else if (表达式成立) {
执行语句;
} else if (表达式成立) {
执行语句;
} else {
执行语句;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 8岁之前:成长阶段
// 8-25之间:求学阶段
// 26-60之间:工作阶段
// 大于60:退休阶段

int a;
cin >> a;

// 多重循环
if (a < 8) {
cout << "成长阶段";
} else if (a >= 8 && a <= 25) {
cout << "求学阶段";
} else if (a >= 26 && a <= 60) {
cout << "工作阶段";
} else {
cout << "退休阶段";
}

2. 嵌套判断

在某一个成立的条件下,是否还有不同的另外的条件判断呢,此时需要用到嵌套判断

1
2
3
4
5
6
7
8
9
10
11
12
13
} else {
cout << "退休阶段,请输入孙子孙女数量:";
// 如果退休阶段有很多个孙子孙女,那还是需要打工赚压岁钱的
int b;
cin >> b;
if (b >= 7 && b <=9) {
cout << "无法退休,要打两份工";
} else if (b >=3 && b <= 6) {
cout << "无法退休,要打一份工";
} else if (b < 3) {
cout << "退休生活";
}
}

习题七:P5715.三位数排序

P5715.三位数排序

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int a,b,c;
cin >> a >> b >> c;

// 多重判断,从小到大排序
// 无非是:abc,acb,bca,bac,cab,cba 这六种
// 输出的值需要空格 a b c 这样;用printf更方便
if (a <= b && a <=c) {
// a最小
if (b <= c)
printf("%d %d %d", a, b, c);
else
printf("%d %d %d", a, c, b);
} else if (b <= a && b <= c) {
// b最小
if (a <= c)
printf("%d %d %d", b, a, c);
else
printf("%d %d %d", b, c, a);
} else if (c <= a && c <= b) {
// c最小
if (a <= b)
printf("%d %d %d", c, a, b);
else
printf("%d %d %d", c, b, a);
}

习题八:1667.最大和最小数的差

1667.最大和最小数的差

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int n, a, b, c, maxn, minn;
cin >> n;

// 拆位
a = n / 100;
b = n / 10 % 10;
c = n % 10;

// 求最大值
maxn = -1;
if (a >= b && a >= c) {
maxn = a;
} else if (b >= c) {
maxn = b;
} else {
maxn = c;
}

// 求最小值
minn = 10;
if (a <= b && a <= c) {
minn = a;
} else if (b <= c) {
minn = b;
} else {
minn = c;
}

// 差
cout << maxn - minn;

习题九:P1909.NOIP2016.普及组.买铅笔

P1909.NOIP2016.普及组.买铅笔

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;

const int N=1e6+10;

int T,n,m;

int main()
{
cin>>n;

int minv=1e9+7;
for(int i=0;i<3;i++){
int a,b;
cin>>a>>b;
minv=min(minv,((n-1)/a+1)*b);
}
cout<<minv<<endl;

return 0;
}

九、switch分支

1. swtich分支

  • 除了if … else条件分支,还有一种 switch … case 分支,用于变量均为等于成立的分支方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // case 值1 表示 变量名 == 值1,成立的话,执行下面缩进的语句
    // break; 表示跳出switch,否则会继续判断后续的case;
    // default 相当于 else,在没有满足条件的时候执行,当然,可以省略这个;
    switch (变量名) {
    case1:
    执行语句;
    break;
    case2:
    执行语句;
    break;
    case3:
    执行语句;
    break;
    ...

    default:
    执行语句;
    }

2. 垃圾分类

  • 王二狗要下楼倒垃圾,垃圾分类有四种,他到底应该倒哪个桶呢?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 垃圾种类
    int n;
    // 输入提示
    cout << "请输入垃圾种类的序号:" << endl;
    cout << "1.表示可回收垃圾!" << endl;
    cout << "2.表示有害垃圾!" << endl;
    cout << "3.表示厨余垃圾!" << endl;
    cout << "4.表示其它垃圾!" << endl;
    cout << "请输入1-4:";
    cin >> n;

    // switch分支
    switch (n) {
    case 1:
    cout << "王二狗往可回收垃圾桶倒了垃圾!";
    break;
    case 2:
    cout << "王二狗往有害垃圾桶倒了垃圾!";
    break;
    case 3:
    cout << "王二狗往厨余垃圾桶倒了垃圾!";
    break;
    case 4:
    cout << "王二狗往其它垃圾桶倒了垃圾!";
    break;
    default:
    cout << "你往哪里倒???倒错了吧!";
    break;
    }

十、for循环.累加和

1. for语句

计算机最大的一个特性就是快速的重复执行有规律的运算,for循环语句 就是做这种事的

1
2
3
for (循环变量初始化;循环条件;循环变量增或减) {
循环体
}

循环流程图

  • 使用 for 循环语句,输出1-10这十个数字:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 输出1-10,每个数字换行
    // int i = 1; 循环变量初始化,从1开始
    // i <= 10; 循环判断,当i目前小于等于10时,继续执行循环体
    // i++;让循环变量进行累加,效果为:i = i + 1,没有这个过程就死循环啦
    // cout << i << endl; 循环体,也就是可执行语句的部分

    // 流程图文字版如下:
    // 第一步:int i = 1;初始化循环变量,这条语句只执行一次
    // 第二步:i <= 10; 判断循环条件;
    // 第三步:cout << i << endl; 第二步条件成立后,执行这一步,不成立,退出for语句;
    // 第四步:i++;增值变量;然后回到第二步继续
    // 后续一直循环2,3,4步,直到退出for语句位置;
    for(int i=1;i<=10;i++){
    cout << i << endl;
    }
    cout << "退出 for 循环后执行我!";

2. 累加和

  • 通过 for 循环 代码将一个月的零花钱保存起来,1号存1块,2号存2块…30号存30块,共存了多少钱。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 存钱变量
    int sum = 0;

    // 累加和
    for(int i=1;i<=30;i++){
    // sum = sum + i;
    sum += i;
    }

    // 输出
    cout << sum;

习题十:1700.输出两位数中含2的整数

1700.输出两位数中含2的整数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
// 十位 个位
int a, b;

// 循环10-99之间,包含10和99
for(int i=10;i<=99;i++){
// 拆位
a = i / 10;
b = i % 10;

// 判断
if (a == 2 || b == 2) cout << i << endl;
}

习题十一:1058.求三位数的水仙花数

1058.求三位数的水仙花数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
// 百 十 个
int a, b, c;

// 循环三位数 100-999
for(int i=100;i<=999;i++){
// 拆位
a = i / 100;
b = i / 10 % 10;
c = i % 10;

// 立方和
if (a * a * a + b * b * b + c * c * c == i) cout << i << endl;
}

习题十二:1395.小丽找数?

1395.小丽找数?

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 整数
int n, a, b, c, d, count = 0;
cin >> n;

// 循环1-n
for(int i=1;i<=n;i++){
// 拆位
a = i / 1000;
b = i / 100 % 10;
c = i / 10 % 10;
d = i % 10;

// 判断需求
if ((a + b + c + d) % 2 != 0 && (a + b + c + d) % 5 != 0) count++;
}

// 输出
cout << count;

十一、for嵌套

1. for嵌套

for嵌套是什么意思?for语句的循环体再执行一个for语句吗?没错!

1
2
3
4
5
6
// 先来个简单的需求,完成下列图形
* * * * *
* * * * *
* * * * *
* * * * *
* * * * *
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 首先,开启纬度思想,一维即:一行一条线;二维则:多行多列是个面
// 第一步,先考虑输出一行
for(int i=1;i<=5;i++){
cout << "* ";
}

// 第二步,将一行改成一列试试,为后续扩展铺路
for(int i=1;i<=5;i++){
cout << "* " << endl;
}

// 第三步,将每一列当成一个for循环,再铺出一行
for(int i=1;i<=5;i++){
for(int j=1;j<=5;j++){
cout << "* ";
}
cout << endl; // 一列换一次行
}

2. 九九乘法表

利用 for循环 嵌套,实现一个不重复的 九九乘法表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 第一步:先考虑1x1=1...1x9=9 这一列
for(int i=1;i<=9;i++){
printf("1x%d=%d\n", i, 1 * i);
}

// 第二步:将输出的每一项换成for循环输出9项
// 循环逻辑:
// i = 1时,j = 1 2 3 4 5 6 7 8 9
// i = 2时,j = 1 2 3 4 5 6 7 8 9
// ...
// i = 9时,j = 1 2 3 4 5 6 7 8 9
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
printf("%dx%d=%2d ", i, j, i * j);
}
printf("\n");
}

// 第三步:去掉右上部分的重复, 1 x 2 = 2 和 2 x 1 = 2 这种
// 问题:每次j都循环满9次是导致重复的根源,如何处理?
// 解题:第1行,后8个是重复的,第2行,后7个是重复的,以此类推;
// 编码:那么只要将每次循环的次数和i保持一致即可。
for(int i=1;i<=9;i++){
for(int j=1;j<=i;j++){
printf("%dx%d=%2d ", i, j, i * j);
}
printf("\n");
}

习题十三:1492.空心正方形

1492.空心正方形

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 正方形边数
int n;
cin >> n;

// 循环1-n
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
// 第一行和第n行是满星
// 第一列和第n列也要星
if (i == 1 or i == n or j == 1 or j == n)
cout << "*";
else
cout << " ";
}
cout << endl;
}

习题十四:1006.打印星号三角形

1006.打印星号三角形

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 行数
int n;
cin >> n;

// 最外层控制行数,比如样例的5行
for(int i=1;i<=n;i++) {
// 中层控制三块三角形
for(int j=1;j<=3;j++){
// 假设第一次循环
// 第一行星号前后有4个空格,那就需要循环4次,星号1次
// 以此类推:第二行,前后3个空格,星号3次
for(int z=1;z<=n-i;z++){
cout << " ";
}
// 星号是:1 3 5 每行+2:i*2-1
// i = 1 z<=1*2-1=1
// i = 2 z<=2*2-1=3
// i = 3 z<=3*2-1=5
for(int z=1;z<=i*2-1;z++){
cout << "*";
}
for(int z=1;z<=n-i;z++){
cout << " ";
}
}
cout << endl;
}

习题十五:1019.求1!+2!+…+N!

1019.求1!+2!+...+N!

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 整数n, 总和sum
int n, sum = 0;
cin >> n;

// 循环1-n
for(int i=1;i<=n;i++){
// 希望累加的过程
// i = 1, 1
// i = 2, 1 x 2
// i = 3, 1 x 2 x 3
// i = 4, 1 x 2 x 3 x 4
// 最后,把每一行乘积的结果累加
int temp = 1;
for(int j=1;j<=i;j++){
// 1 * 1 * 2 * 3 * 4
temp *= j;
}
sum += temp;
}

cout << sum;

习题十六:1519.求1~n中每个数的因子

1519.求1~n中每个数的因子

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 整数n
int n;
cin >> n;

// 循环1-n
for(int i=1;i<=n;i++){
// 输出
cout << i << ":";
// 循环1-i
for(int j=1;j<=i;j++){
// 求因子
if (i % j == 0) cout << j << " ";
}
// 换行
cout << endl;
}

十二、变量作用域.退出循环

1. 变量作用域

变量作用域分为局部和全局,在之前的解题中存在不同位置,效果也不尽相同;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 首先,函数还没学过,不考虑

int a = 5;
// 在if里面声明的变量和外部的区别
if (5 > 3) {
// 判断体内可以访问外部变量a
// 此时a可以理解为全局变量(不考虑函数),在哪里可以访问(赋值取值)
cout << a;
}

// 在if体内的变量
if (5 > 3) {
int b = 3;
cout << b;
}

// 访问不了b,因为它是局部变量,只能在所在的if体内访问
cout << b;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 这里int i属于for体内的局部变量i
for(int i=1;i<=5;i++){
//...
}

// 外部无法访问,直接报错
cout << i;

// 可以提到外面来声明来保证全局性
int i;
for(i=1;i<=5;i++){
//...
}

// 可以访问,并且是6,因为for最后执行是i++
cout << i;

// 之前短除法的临时变量
for(int i=1;i<=10;i++){
// 这里的tmp在for外部就无法访问了
// 它就是临时工,在for体内用的
// 所以总结:想要在体内用不想在外面用,就在体内声明
// 如果要全局都能使用,就在外部声明,比如程序代码的第一行
int tmp = 0
}

2. break 退出

break 的作用是在循环体内,可设置条件来退出这个循环;

1
2
3
4
5
6
7
// break退出
for(int i=1;i<=10;i++){
// 当遇到5退出整个循环
if (i == 5) break;
// 输出i
cout << i << endl;
}

3. continue 退出

continue 的作用是在循环体内,可设置条件来退出当前这次循环,然后继续执行下一次循环;

1
2
3
4
5
6
7
// continue退出
for(int i=1;i<=10;i++){
// 当遇到5退出当前循环,continue需要卸载循环体最前面才有意义
if (i == 5) continue;
// 输出i
cout << i << endl;
}

习题十七:P1075.NOIP2012.普及组.质因数分解

P1075.NOIP2012.普及组.质因数分解

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 质数 = 素数,只能被1和本身整除
// n
int n;
cin >> n;

// 循环2~n-1
for(int i=2;i<=n-1;i++){
// 判断i是否为因子,找到第一个即最小的因子
// 天坑:此题如果测试数据n是20,那因子为2,10,则10最大,但10不是质数
// 审题:题目说了由两个不同质数的乘积,10不是质数,所以测试数据不会给20让你测
// 再审题:由两个不同的质数,不是多个,还必须是质数。。。20 不符合要求的
if (n % i == 0) {
// 求出较大的数
cout << n / i;
break;
}
}

习题十八:P5725.求三角形

P5725.求三角形

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// n
int n;
cin >> n;

// 循环1-n*n
for(int i=1;i<=n*n;i++) {
// 个位数,补零
if (i < 10) cout << 0 << i;
else cout << i;

// 判断n的倍数,换行
if (i % n == 0) cout << endl;
}

// 空一行
cout << endl;

// 循环1-n,负责n列,计数器count
int count = 1;
for(int i=1;i<=n;i++){
// 输出空格
for(int j=1;j<=n-i;j++){
cout << " ";
}
// 输出数字
for(int j=1;j<=i;j++){
if (count < 10) cout << 0 << count;
else cout << count;
count++;
}
cout << endl;
}

习题十九:P2669.NOIP2015.普及组.金币

P2669.NOIP2015.普及组.金币

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// k天数, coin金币, day当前累计天
int k, coin = 0, day = 0;
cin >> k;

// 简单捋一下
// i=1, 第1天:+1
// i=2, 第2,3天:+2 +2
// i=3, 第4,5,6天:+3 +3 +3
// ...

// 现在给出的k是天数

// 根本不清楚i的末值是多少,那就无限循环吧(死循环)
// 死循环可以将条件去掉,但保留分号,循环体通过判断退出即可
// 外层循环,表示轮次,第1次1天,第2次2,3天,第3次4,5,6天中的次
for(int i=1;;i++) {
// 将轮次中的天数累加到day里
for(int j=1;j<=i;j++){
// 累加天数
day++;
// 累加金币
coin += i;

// 退出机制
if (day == k){
cout << coin << endl;
return 0;
}
}
}

十三、while和do while循环

1. while

while 循环相当于 for 循环,可以不用计数器,需要先进行条件判断,也可设置死循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 死循环:条件是1或者true,让它永远成立即可
while(1){
cout << 123;
}

// 利用计数器输出1-10
int a = 1;
while(a <= 10) {
cout << a << endl;
a++;
}

// 死循环中途退出
// 支持break、continue
int a = 1;
while(1){
if (a == 5) break;
cout << 123;
a++;
}

2. do while

do while 循环相当于 while 循环,可以将条件判断至于循环体下方,让其无条件先循环一次

1
2
3
4
5
// 不管条件是否满足,至少要执行一次循环体
int a = 1;
do{
cout << 123;
} while(a < 0);

习题二十:1062.求落地次数

1062.求落地次数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
// m高度,审题有0.5,所以是浮点型
float m = 100;
// count计数器
int count = 0;

// 大于等于0.5就一直循环
while(m >= 0.5){
count++;
m /= 2.0;
}

cout << count;

习题二十一:1261.韩信点兵

1261.韩信点兵

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 人数
int n = 0;

// 不知道人数,只能无脑死循环
while(1){
// 每循环一次n+1,统计人数
n++;

// 余数同时成立即可
if (n % 5 == 1 && n % 6 == 5 && n % 7 == 4 && n % 11 == 10) {
cout << n;
break;
}
}

十四、短除法.新式拆位

1. 短除拆位

之前我们拆位使用了整除+求余的方式,但对于不确定位数的整数就会捉襟见肘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 要求输入三至五位整数
// 将每一位输出
int n;
cin >> n;

// 使用短除法
// 通过求余得到末尾:
// 比如 123 % 10 = 3
// 然后 12 % 10 = 2 ... 1 % 10 = 1...最终除数为0
while(n != 0) {
// 输出拆位
cout << n % 10 << endl;
// 整除
n /= 10;
}

习题二十二:1750.有0的数

1750.有0的数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int n, count = 0;
cin >> n;

// 循环1-n,1-9不含0,直接从10开始
for(int i=10;i<=n;i++){
// 短除
int temp = i;

while(temp != 0) {
// 102: 2..0(+1)..1
if (temp % 10 == 0) {
count++;
break; // 只要包含0就行了,退while
}

// 整除
temp /= 10;
}
}

cout << count;

习题二十三:P1307.NOIP2011.普及组.数字反转

P1307.NOIP2011.普及组.数字反转

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// n原数,f新数
int n, f = 0;
cin >> n;

// 不用考虑负数
while(n != 0){
// 拆位
int g;
g = n % 10;

// 假设123,上一轮f得到了3,这次要30+2=32,最后要320+1=321
// 第一轮,由于f是0,所以,f * 10 + g = 3
f = f * 10 + g;

// 整除
n /= 10;
}

cout << f;

习题二十四:P1980.NOIP2013.普及组.计数问题

P1980.NOIP2013.普及组.计数问题

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int n, x, count = 0;
cin >> n >> x;

// 循环1-n
for(int i=1;i<=n;i++){
//拆位
int temp = i;

while(temp != 0) {
if (temp % 10 == x) count++;
temp /= 10;
}
}

cout << count;

习题二十五:P1089.NOIP2004.提高组.津津的储蓄计划

P1089.NOIP2004.提高组.津津的储蓄计划

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// money钱, cost花销, mather妈
int money = 0, cost = 0, mather = 0;

// 1-12月循环
for(int i=1;i<=12;i++) {
// 每个月+300元
money += 300;
// 输入这个月花销
cin >> cost;

// 如果持有100或以上,就把整百交给妈妈
// 比如有180,交给妈妈100,有280,交给妈妈200
// 扣去当月花销
money -= cost;
while (money >= 100) {
mather += 100;
money -= 100;
}

// 如果发现钱是负的
if (money < 0) {
printf("-%d", i);
return 0;
}
}

// 输出money
cout << money + mather + (mather * 0.2);

十五、简单穷举

1. 穷举说明

穷举即枚举,我们会在下一套课程中讲解 暴力枚举,那么现在先简单的了解下

  • 其实,在之前循环课程中,我们经常使用穷举法,只是没有挑明:
    • 通过不断循环,让大量的值进行比对验证,得出想要的结果;

2. 买公园门票

1
2
3
4
5
6
7
8
9
10
11
// 假设只有一个小孩,成人拥有的票钱 = 40 - 3
// 成人票最多买:37 / 8 = 4张,循环1-4张
for(int i=1;i<=(40-3)/8;i++) {
// 第一次循环:1张成人票,减去-8,再看看买小孩的票,能否整除
// 第i次以此类推
int x;
// 得到买小孩票的钱
x = 40 - i * 8;
// 判断是否能整除
if (x % 3 == 0) cout << i << " " << x / 3 << endl;
}

习题二十六:1025.兑换硬币

1025.兑换硬币

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 100分, 累计count
int f = 100, count = 0;

// 先从5分开始,1分2分至少一个:(100 - 2 - 1) / 5 = 5分钱个数
for(int i=1;i<=(100-2-1)/5;i++){
// 嵌套2分:(100 - 5 - 1) / 2 = 2分个数
for(int j=1;j<=(100-5-1)/2;j++){
// i第一轮,j第一轮:一个5分+一个2分,93个1分
// i第一轮,j第二轮:一个5分+两个2分,91个1分
// i第一轮,j第三轮:一个5分+三个2分,89个1分
// ...
// i第三轮,j第一轮:三个5分+一个2分,83个1分
// ...
// i第三轮,j第五轮:三个5分+五个2分,75个1分
// ...


// 得到1分钱银币的总分数
// 在循环中,值>=1,说明可以整除1
if (f - (i * 5 + j * 2) >= 1) count++;
}
}

cout << count;

十六、批量存储.一维数组

1. 数组声明

数组分为:一维、二维、多维,大部分情况下只用一维,少量二维,多维很少

  • 变量只能存储一条数据,数组可以存储多条数据;

  • 比如我要声明一个可以存放10条数据数组:

    • 格式为:类型名 数组名[表达式] 注:[] 可以是数字,也可以是a-1的运算;
    1
    2
    // 声明可以存放10条数据的数组,数组里的值(或叫元素)为整数
    int a[10];
    a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
    78 59 66 12 9 128 98 73 52 76
  • 从上述的数组数据结构图中,总结几点:

    • int a[10]:表示声明一个可以存放10条整数的数组a;
    • 其中[0~9],我们称为下标,它的范围是0-9之间;
    • 对某一个下标进行赋值取值,可以是:a[5] = 128;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 对数组a下标5,也就是第6个位置赋值
    a[5] = 128;
    // 输出123
    cout << a[5];

    // 没有赋值的数组会随机出现不同值
    cout << a[3];

    // 当然这里有一个目前还没有学习的概念:全局变量和局部变量
    // 如果把int a[10]放在int main()外部,就是全局,那么不赋值默认就是0
    // 如果是局部变量:没有赋值的还有可能默认0,我们可以用循环查看
    for(int i=0;i<=19;i++){
    printf("a[%d]=%d\n", i, a[i]);
    }
  • 第二种数组的赋值方式,采用花括号对声明的数组直接赋值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 花括号直接赋值
    int a[10] = {78, 59, 66, 12, 9, 128, 98, 73, 52, 76};
    // 通过循环逐步输出,注意,i从0开始,i<=9或<10
    for(int i=0;i<=9;i++){
    printf("a[%d]=%d\n", i, a[i]);
    }

    // 如果你设置了a[20],但{}里只有10个数据,则会用整数0填充
    for(int i=0;i<=19;i++){
    printf("a[%d]=%d\n", i, a[i]);
    }
  • 如果不确定赋值的个数,可以采用动态方案赋值:

    1
    2
    // 但这样,就无法得知长度了;需要通过以后学习的sizeof()函数来获取,先不深入探讨
    int a[] = {1, 2, 3, 4, 5}
  • 至于无法一开始初始化指定值,又怕未赋值导致随机值,可以先初始化零;

    1
    2
    3
    4
    // 初始化0:方案一
    int a[10] = {};
    // 初始化0:方案二
    int a[10] = {0};

2. 下标越界

什么是下标越界:声明时,超过了指定的范围就是下标越界,规定:不要越界;

  • 下标越界,有些平台会报异常,有些不报错(这种最要命,程序没错,数据错了检查不出来)

  • 所以,不要越界;

  • 比如,你int a[10],你赋值时就初始化时,就无法对a[20]赋值,导致随机数字;

    1
    2
    int a[10] = {0};
    cout << a[12];

习题二十七:1153.查找“支撑数”

1153.查找“支撑数”

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 整型数组,长度100,默认0,废弃a[0],+1
int a[101] = {0};
int n;
cin >> n;

// 循环1-n,注意:如果从1开始,废弃a[0]
// 循环0-n,注意:i<n 去掉等号
for(int i=1;i<=n;i++) cin >> a[i];

// 支撑数:不在第一位,不在最后一位,掐头去尾
for(int i=2;i<=n-1;i++){
// 当前值比左右两边都大
// 上一个值:a[i-1],下一个值:a[i+1]
if (a[i] > a[i-1] && a[i] > a[i+1]) cout << a[i] << endl;
}

习题二十八:1354.拿到某个数的概率

1354.拿到某个数的概率

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 球
int a[101] = {0}, n, cnt = 0;
cin >> n;

// 赋值
for(int i=1;i<=n;i++) cin >> a[i];

// 对比值
int x;
cin >> x;

// 统计
for(int i=1;i<=n;i++){
// 判断
if (a[i] == x) cnt++;
}

// 求概率
printf("%.2f", cnt / float(n));

习题二十九:2029.缺失的数字

2029.缺失的数字

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 数
int a[100001] = {0}, n, x;
cin >> n;

// 循环1-n
for(int i=1;i<=n-2;i++){
// x
cin >> x;
// 将a数组中下标为x,即a[x]做个标记,比如设置为1
a[x] = 1;
}

// 再次循环1-n
for(int i=1;i<=n;i++){
// 输出值为零的数组元素
if (a[i] == 0) cout << i << " ";
}

习题三十:P1047.NOIP2005.普及组.校门外的树

P1047.NOIP2005.普及组.校门外的树

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int l, m, a[10001] = {0};
cin >> l >> m;

int u, v = 0;
// m行
for(int i=1;i<=m;i++){
cin >> u >> v;
// 从u点到v点
for(int j=u;j<=v;j++){
a[j] = 1;
}
}

int total = 0;
// l总树木,题目说从0开始
for(int i=0;i<=l;i++){
if (a[i] == 0) total++;
}

// 输出
cout << total;

十七、二维数组.多维数组

1. 数组声明

所谓的二维数组,就是数组里还有一个数组,类似嵌套循环,多维数组就是数组里的数组里的数组;

  • 声明格式:

    1
    数据类型 数组名[行数][列数]		// 就是两层嵌套循环
  • 二维数组构建一个矩阵表格:

    a[0,0] -> 78 a[0,1] -> 102 a[0,2] -> 44
    a[1,0] -> 92 a[1,1] -> 165 a[1,2] -> 92
    a[2,0] -> 67 a[2,1] -> 278 a[2,2] -> 39
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // 上面的构建,从0开始的,也可以和之前废弃0从1开始
    int a[3][3];

    // 硬赋值
    a[0][0] = 78;
    a[0][1] = 102;
    a[0][2] = 44;
    a[1][0] = 92;
    a[1][1] = 165;
    a[1][2] = 95;
    a[2][0] = 67;
    a[2][1] = 278;
    a[2][2] = 39;

    // 二维数组较好理解的初始化,内嵌的花括号表示一行,逗号结束表示换行
    int b[3][3] = {{78, 102, 44}, {92, 165, 95}, {67, 278, 39}}

    // 二维数组本质也是一维数组,可按照一维方式赋值
    int b[3][3] = {78, 102, 44, 92, 165, 95, 67, 278, 39};

    // 嵌套循环输出
    // 输出矩阵为:
    // 第一轮:a[0][0] a[0][1] a[0][2]
    // 第二轮:a[1][0] a[1][1] a[1][2]
    // 第三轮:a[2][0] a[2][1] a[2][2]
    for(int i=0;i<=2;i++){
    for(int j=0;j<=2;j++){
    printf("%5d", a[i][j]);
    }
    printf("\n");
    }

2. 多维数组

多为数组,三维:数组里的数组里的数组,不用理解到其它学科的纬度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 题外话:不用理解成几何里三维立体,四维超立体,或天文里的四维时空,没那么复杂
// 用三维表示地球村,一维:地球,二维:国家:三维:省份,用数字代替
int b[3][3][3] = {
{
{1, 2, 3}, {4, 5, 6}, {7, 8, 9}
},
{
{11, 11, 12}, {13, 14, 15}, {16, 17, 18}
},
{
{20, 21, 22}, {23, 24, 25}, {26, 27, 28}
}
};

for(int i=0;i<=2;i++){
for(int j=0;j<=2;j++){
cout << "( ";
for(int z=0;z<=2;z++){
cout << b[i][j][z] << " ";
}
cout << "), ";
}
cout << endl;
}

习题三十一:1272.郭远摘苹果

1272.郭远摘苹果

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int n, m, a[11][11] = {0};
cin >> n >> m;

// i从0开始也行,从1开始也行
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cin >> a[i][j];
}

// 最大值没给范围,那就把第一个值初始
int maxn = a[1][1], minn = a[1][1];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
// 求最大值
if (maxn < a[i][j]) maxn = a[i][j];
// 求最小值
if (minn > a[i][j]) minn = a[i][j];
}
}

// 求差
cout << maxn - minn;

习题三十二:1384.靶心数

1384.靶心数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int n, m, a[101][101] = {0};
cin >> n >> m;

// 赋值
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cin >> a[i][j];
}

// 判断
// 第一行:[1,1] [1,2] [1,3] [1,4]
// 第二行:[2,1] [2,2] [2,3] [2,4]
// 第三行:[3,1] [3,2] [3,3] [3,4]
// 第四行:[4,1] [4,2] [4,3] [4,4]

// 已知第一行,末行,第一列,末列不存在靶心,去除减少循环次数
for(int i=2;i<=n-1;i++){
for(int j=2;j<=m-1;j++){
//将上下左右相邻的判断一下
if (a[i][j] > a[i-1][j] && a[i][j] > a[i][j-1] && a[i][j] > a[i][j+1] && a[i][j] > a[i+1][j])
cout << a[i][j] << endl;
}
}

习题三十三:P5731.蛇形方阵

P5731.蛇形方阵

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
int n, a[10][10] = {0};
cin >> n;

// 假设n=4
// a[1,1] a[1,2] a[1,3] a[1,4]
// a[2,1] a[2,2] a[2,3] a[2,4]
// a[3,1] a[3,2] a[3,3] a[3,4]
// a[4,1] a[4,2] a[4,3] a[4,4]


// 初始化行和列,
int i = 0, j = 0;
// 假设n=4,则执行16次,累加可以在循环体执行
for(int z=1;z<=n*n;){
// 朝右
i++;j++;
while(j <= n && !a[i][j]){
a[i][j] = z;
j++;z++;
}
// 朝下,i下移一位,j左移一位,保证不越界
i++;j--;
while(i <= n && !a[i][j]){
a[i][j] = z;
i++;z++;
}
// 朝左,i上移一位,j左移一位
i--;j--;
while(j >= 1 && !a[i][j]){
a[i][j] = z;
j--;z++;
}
// 朝上,j右移一位
i--;j++;
while(i >= 1 && !a[i][j]){
a[i][j] = z;
i--;z++;
}
}

for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%3d", a[i][j]);
}
printf("\n");
}

习题三十四:P5732.杨辉三角

P5732.杨辉三角

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 杨辉三角形特性
// 1.第一行只有数字:1
// 2.每一行头尾数字:1
// 3.每一个非头尾数字是上一行对应左右的和:
// 比如a[5][3] = a[4][2] + a[4][3]
// 4.第n行数字的和为:2的n-1次方
// 1+5+10+10+5+1=32 为: 2 x 2 x 2 x 2 x 2 = 32

// 1
// 1 1
// 1 2 1
// 1 3 3 1
// 1 4 6 4 1
// 1 5 10 10 5 1

// 为了方便观察,改为以上图形,程序上不需要考虑左边空隙

int n, a[21][21] = {0};
cin >> n;

// 初始首行
a[1][1] = 1;

// 循环
for(int i=2;i<=n;i++){
// 每一行的列数正好是当前的i数
for(int j=1;j<=i;j++){
a[i][j] = a[i-1][j-1] + a[i-1][j];
}
}

// 输出
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cout << a[i][j] << " ";
}
cout << endl;
}

习题三十五:P5729.工艺品制作

P5729.工艺品制作

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 三维
int a[21][21][21] = {0}, w, x, h, q, ans = 0;

// 输入 长宽高
cin >> w >> x >> h >> q;

// 被激光切割的组数q
while(q--){
int x1, y1, z1, x2, y2, z2;
cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
// 三重循环标记被切割的方块
for(int i=x1;i<=x2;i++){
for(int j=y1;j<=y2;j++){
for(int k=z1;k<=z2;k++){
a[i][j][k] = 1;
}
}
}
}


// 输出三维,求0的个数,即为剩下的方块体积
for(int i=1;i<=w;i++){
for(int j=1;j<=x;j++){
for(int k=1;k<=h;k++){
if (a[i][j][k] == 0) ans++;
}
}
}

cout << ans;

十八、函数.参数.返回值.常量

1. 自定义函数

函数或叫自定义函数,是用户自己定义的一种函数,适用于将大量重复的代码进行封装调用

  • 自定义函数说明:

    • 每个程序都在用函数,比如 int main() 这是主函数,程序在这里自动执行;
    • int main() 中, int是返回类型,自然也会有其它类型,甚至无返回类型void;
    • 函数末尾除了void类型,一般都会有一个return 返回对应类型;
    • 函数名也遵循变量原则;
    • 用户自定义创建的函数,无法像主函数 main() 那样自动执行,需要被调用自行;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 创建一个无返回值的自定义函数,通过调用执行
    // 比如重复输出10组星号三角形

    // 星号三角
    void star() {
    for(int i=1;i<=3;i++){
    for(int j=1;j<=i;j++){
    cout << "*";
    }
    cout << endl;
    }
    }

    int main() {
    for(int i=1;i<=10;i++){
    star();
    }

    return 0;
    }

2. 参数和返回值

函数中有两个重要的概念:参数和返回值,用于更加灵活的使用函数;

  • 参数:

    • 形参(形式参数),在定义函数时定义的变量(类似变量声明,可直接赋值作为初始值);
    • 实参(实际参数),在调用函数时传递的值(类似赋值,直接给形参变量赋值);
    • 定义了形参,实参需要一一对应传递赋值,否则出错;
    • 如果形参设置了默认值,则可以省略,一般定义默认值的形参在最后;
    • 有默认值的形参,如果被实参传递,则覆盖默认值;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 带参数的void类型函数
    // 控制输出的星数
    // num:每行个数
    // line:行数
    // def:显示的字符
    void star(int num, int line, string def = "*") {
    for(int i=1;i<=line;i++){
    for(int j=1;j<=num;j++){
    cout << def;
    }
    cout << endl;
    }
    // 特别注意:函数里声明的变量num,line,def,i,j在star函数体外无法识别
    }

    int main() {
    // 调用,并传参
    star(5, 5);
    star(3, 3, "@");

    return 0;
    }
  • 返回值:

    • void 类型 是无返回值的,上面已经研究过;
    • 需要返回值的类型:比如 int 、float 、string 等;
    • 为什么要返回值?因为可以把函数当成一个变量,参与到主函数程序中的运算中;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 计算一个圆的面积和周长的和

    // 圆的面积:π * r * r
    float area(int r) {
    float pi = 3.14;
    return pi * r * r;
    }

    // 圆的周长:π * r * 2
    float circ(int r) {
    float pi = 3.14;
    return pi * r * 2;
    }

    int main() {
    // 输入半径r
    int r;
    cin >> r;

    // 计算面积+周长的和
    cout << area(r) + circ(r);

    return 0;
    }

3. 常量概念

之前一直在使用变量,即:可以改变的量;而常量是相对的产物,即:定义后就不可改变的量;

  • 常量:

    • 语法多种,推荐一种即可:const 类型 常量名 = 值;
      • const int A = 10; (这里的A为常量名,推荐用大写,以区别变量)
    • 常量是不可改变的量,定义之后无法再次赋值,所以定义时就必须初始化;
    • 为何要使用常量,有一些值是固定的,比如π,如果不小心改变了,语法上不报错就非常要命;
    • 为何要大写常量名,当一段程序有几十上百量时,常量变量识别度变差,故约定大写;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 创建一个常量PI
    const float PI = 3.14;

    // 圆的面积:π * r * r
    float area(int r) {
    return PI * r * r;
    }

    // 圆的周长:π * r * 2
    float circ(int r) {
    return PI * r * 2;
    }

习题三十六:1512.甲乙的年龄

1512.甲乙的年龄

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 1. 甲乙两者年龄只和是两位数
// 2. 这个两位数是一个质数
// 3. 这个质数(个位+十位=13)
// 4. 甲比乙大13

// 求甲乙分别是多少?

// 声明和变量
int sum = 0;

// 枚举(穷举)出所有情况
// 假设乙最小1,那么甲就是14,乙最大就是99-13=86
// 循环1-86之间,但当乙到达44时,和为101,也就是说43是头
for(int i=1;i<=43;i++){
// 求和
sum = i + (i + 13);
//cout << i << " " << sum << endl;

// 判断这个和是不是质数以及数字之和是13
// 先用常规方法,然后改函数,第一次用函数会因缺少经验无从下手

// 标记
bool flag = true;
// 判断质数,只能被1和本身整除
for(int j=2;j<=sum-1;j++){
if (sum % j == 0) {
flag = false;
break;
}
}

// 标记判断
if (flag == false) continue;
//cout << i + 13 << " " << i << " " << sum << endl;

// 判断数字之和13
int a = sum / 10;
int b = sum % 10;

if (a + b == 13) {
cout << i + 13 << " " << i << " " << endl;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 判断质数
bool isZ(int n) {
for(int j=2;j<=n-1;j++){
if (n % j == 0) {
// 只要执行了return 函数就终止执行
return false;
}
}
// 上面循环体没有执行return,就继续执行下面的return
return true;
}

// 判断和
bool isS(int n) {
int a = n / 10;
int b = n % 10;

// 执行了return true将终止往下执行
if (a + b == 13) return true;

// 没有执行true,则继续返回false
return false;
}

int main() {
int sum = 0;

for(int i=1;i<=43;i++){
sum = i + (i + 13);

// 判断输出
if (isZ(sum) && isS(sum))
cout << i + 13 << " " << i << endl;
}
return 0;
}

习题三十七:1862.友好数

1862.友好数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 求约数和
int sum(int n) {
int tmp = 0;
// 1到n-1,题目说除了本身
for(int i=1;i<=n-1;i++){
if (n % i == 0) tmp += i;
}
return tmp;
}

int main() {
int a, b;
cin >> a >> b;

//if ((对a求约数和) == b && (对b求约数和) == a)
if (sum(a) == b && sum(b) == a) cout << "yes";
else cout << "no";

return 0;
}

习题三十八:P5737.闰年展示

P5737.闰年展示

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 之前学习过除了函数的变量作用域
// 1. 我们可以意识到:函数体内的变量,其它函数或外部全局都无法识别
// 2. 外部全局声明的变量,叫做全局变量,所有区域均可识别
// 3. 当然,至于全局和局部变量重名会怎样,可自行测试,建议别重名

// 在和main等函数同一平级声明的变量为全局变量
// 我们要创建一个全局计数器变量cnt;
int cnt = 0, a[1500];

// 判断闰年函数
void isR(int year) {
if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) {
//cnt++;
//cout << year << endl;
// 这里的顺序是a[cnt] = year, 然后cnt++
a[cnt++] = year;
}
}

int main() {
// 题目非常初级,主要学习下函数,实际答题中并不强制
int x, y;
cin >> x >> y;

// 循环
for(int i=x;i<=y;i++){
// 判断闰年,将判断的方式作为函数处理
// 它需要判断后输出,或者不输出,可以用void类型,否则还需要返回判断
isR(i);
}

cout << cnt << endl;
// 输出
for(int i=0;i<cnt;i++){
cout << a[i] << " ";
}

return 0;
}

十九、P5739.计算阶乘.递归入门

P5739.计算阶乘.递归入门

* 代码详解

递归流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 什么是递归?即:通过函数自我执行的方式表现出循环的效果
// 求n!,也就是阶乘,之前做过,这次作为递归入门
// 之前用循环做的,如下:
// i = 1, 1
// i = 2, 1 x 2
// i = 3, 1 x 2 x 3
// i = 4, 1 x 2 x 3 x 4
// i = n, 1 x ... x n


// fn
int fn(int n) {
// 假设n=4,然后递减循环 4 3 2 1
// 递归则不需要再循环
// 递归思维:n x (n - 1) x (n - 2) x (n - 3) ...

// 理 解:return n * fn(n - 1);
// 调 用:fn(4)
// 第一轮:return 4 * fn(3),fn(3)又调用了自己,就进入第二轮
// fn(3)这块本身是:return 3 * fn(n-1) 也就是:3 * fn(2)

// 第二轮:return 4 * 3 * fn(2)
// 第三轮:return 4 * 3 * 2 * fn(1) 1到头了,单独判断返回1即可
// ...
// 最终轮:fn(1),判断if n == 1 return 1即可,最终为:return 4 * 3 * 2 * 1


// 先判断1停止递归
if (n == 1) return 1;

// 递归
return n * fn(n - 1);
}

int main() {
int n;
cin >> n;

// 递归求n!
cout << fn(n);
return 0;
}

习题三十九:1514.数根

1514.数根

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 树根
int fn(int n) {
int sum = 0;
// 短除
while(n != 0){
sum += n % 10;
n /= 10;
}

// 判断sum是否是个位数
if (sum < 10) return sum;

// 递归
return fn(sum);
}

int main() {
int n;
cin >> n;

// 调用函数
cout << fn(n);
return 0;
}

习题四十:1223.汉诺塔的移动次数

1223.汉诺塔的移动次数

* 代码详解

image-20240125161802209
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 1. 移动只能一块一块移动
// 2. 大的不能放到小的上面
// 3. 最终状态从A到C状态,如上图

// 手动计步如下:
// 1块:1步
// 2块:3步
// 3块:7步
// 4块:15步

// 要求输入n块,<= 20,求多少步
// 已知n == 1,那么就 1 步
// 假设n == 2,先将小块移动到B,花了1步,大块移动到C花了1步,小块再移动到C;
// 结论:小块2步 + 大块1步 = 3步

// 假设n == 3,先将小块移动到C,花了1步,中块移动到B,花了1步,小块移动到B,花了2步
// 大块移动到C,花了1步,小块移动到A,花了3步,中块移动到C,花了2步
// 小块移动到C,花了4步
// 结论:小块4步 + 中块2步 + 大块1步 = 7步

// 假设n == 4,结论:8 + 4 + 2 + 1 = 15,找到规律:每次都是x2的步数,也就是2的n次方-1
// 用这种规律直接:2 * 2 * 2 * 2 - 1 = 15 步;

// 非常有意思的事,深入研究规律后,直接循环做出来了。根本无需递归
int n;
cin >> n;

int step = 1;
// 输出
for(int i=1;i<=n;i++){
step *= 2;
}

cout << step - 1;

// 那我们再思考用递归来做一下:
// 步数:n = 1, 2, 3, 4 步 = 1, 3, 7, 15
// 公式:2 * fn(n - 1) + 1,fn(n - 1)是上一层,x 2 + 1 = 本层步数
// 代码:return 2 * fn(n - 1) + 1,方便理解,列出轮次:
// 第一轮:return 2 * fn(3) + 1
// 第二轮:return 2 * (2 * fn(2) + 1) + 1
// 第三轮:return 2 * (2 * (2 * fn(1) + 1) + 1) + 1
// 第四轮:return 2 * (2 * (2 * 1 + 1) + 1) + 1 = 15

// 汉诺塔
int fn(int n) {
// n == 1
if (n == 1) return 1;

return 2 * fn(n - 1) + 1;
}

int main() {
int n;
cin >> n;

cout << fn(n);
return 0;
}

习题四十一:P5743.猴子吃桃

P5743.猴子吃桃

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 1. 每一天吃当前数量的一半+1个
// 2. 第n天发现只剩1个
// 3. 求共多少个

// 假设n == 4,剩1个
// 没有思路就先猜,第3天,大概是4个,吃一半加1个,正好剩1个,这个很容易猜到;
// 但我们可能要cin >> n,fn(n);输入4,不可能判断n == 4 return 1;
// 但我们知道n == 1是第一天,我们要反向思考这题:
// 原本:1:22 2:10 3:4 4:1 从后向前推:(当前 + 1) * 2
// 反向:1:1 2:4 3:10 4:22
// 换成递归格式为:return (fn(n-1) + 1) * 2,并且 n == 1 return 1;
// 轮次列表:
// 第一轮:return (fn(3) + 1) * 2
// 第二轮:return (((fn(2) + 1) * 2) + 1) * 2
// 第三轮:return (((((fn(1) + 1) * 2) + 1) * 2) + 1) * 2
// 第四轮:return (((((1 + 1) * 2) + 1) * 2) + 1) * 2 = 22

// 猴子吃桃
int fn(int n) {
// 界限
if (n == 1) return 1;

// 逆推
return (fn(n-1) + 1) * 2;
}

int main() {
int n;
cin >> n;

// 递归
cout << fn(n);
return 0;
}

习题四十二:1148.数数小木块

1148.数数小木块

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 先硬算,然后找规律
// n:块数
// 1:1 2:3 3:6 4:10 5:15
// 1:1
// 2:1+2
// 3:1+2+3
// 4:1+2+3+4
// 5:1+2+3+4+5

// 方块
int fn(int n) {
// 界限
if (n == 1) return 1;

// 上一层的个数 + 本层多出来的 = 本层个数
return fn(n - 1) + n;
}

int main() {
int n, sum = 0;
cin >> n;

// 题目要求将所有层数将每一层累加
for(int i=1;i<=n;i++){
sum += fn(i);
}
cout << sum;
return 0;
}

二十、常用内置函数

1. 数学函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <cmath> // 数序函数库 或math.h
using namespace std;

int main() {
// 绝对值
cout << abs(-5) << endl;
// 向上取整,有小数进一位,5.11=6
cout << ceil(5.11) << endl;
// 向下取整,截断取,5.95=5
cout << floor(5.95) << endl;
// 四舍五入
cout << round(5.55) << endl;
// 平方根
cout << sqrt(16) << endl;
// 幂函数,10的4次方的值
cout << pow(10, 4) << endl;
// 自然常数e为:2.71828...
// exp(n)为指数函数,获取e的n次方,1次方即自己
cout << exp(1) << endl;
// 对数函数log(n),即幂的逆运算
// 获取以e为底的n的对数
cout << log(exp(3)) << endl;

// 还有sin(正弦)、cos(余弦)、tan(正切)启蒙涉及不到


return 0;
}

2. 比较函数

1
2
3
4
// 取最小值
cout << min(1, 9) << endl;
// 取最大值
cout << max(1, 9) << endl;

习题四十三:P5735.距离函数

P5735.距离函数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 用函数减少重复
float dis(float a, float b, float c, float d) {
return sqrt((a - b) * (a - b) + (c - d) * (c - d));
}

int main() {
float x1, x2, x3, y1, y2, y3, sum = 0;
cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;

// 两点距离:
sum += dis(x2, x1, y2, y1);
sum += dis(x3, x2, y3, y2);
sum += dis(x3, x1, y3, y1);

// 输出
printf("%.2f", sum);

return 0;
}

习题四十四:1253.寻找肇事司机

1253.寻找肇事司机

1. 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <math.h>
using namespace std;

int main() {
int a, b, c, d;
// 循环四位数
for(int i=1100;i<=9988;i++){
a = i / 1000;
b = i / 100 % 10;
c = i / 10 % 10;
d = i % 10;



// 判断
// 四位的车号刚好是一个整数的平方
// sqrt(16) = 4,怎么判断4的平方是16
// sqrt(26) = 5.xxx sqrt(25) = 5
// 4 x 4 = sqrt(16) * sqrt(16)
// floor(sqrt(26)) = 5 ?= sqrt(26) = 5.xxx
if (a == b && c == d && a != c && sqrt(i) == floor(sqrt(i))) {
cout << i << endl;
}
}
return 0;
}

习题四十五:P5736.质数筛

P5736.质数筛

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 判断质数
bool isP(int x) {
// 1不是质数,直接返回0。
if (x == 1) return 0;
// 循环验证质数,1和本身不参数,2~x-1
// for(int i=2;i<=x-1;i++)
// 优化算法:
// 1.假设x = 16,它不是质数,因子有:2, 4, 4, 8
// 2.假设x = 20,它不是质数,因子有:2, 4, 5, 10
// 3.假设x = 30,它不是质数,因子有:2, 3, 5, 6, 10, 15
// 寻找规律:
// 1.这些因子的最大值,基本是数值的一半,i<=x/2即可;
// 2.这些因子中间来一刀,左右两侧是配对的。比如x=30,只要循环到5-6之间即可,sqrt(30)

// 循环验证
for(int i=2;i<=sqrt(x);i++){
if (x % i == 0) return false;
}
return true;
}

int main() {
int n;
cin >> n;

// 循环1-n赋值
for(int i=1;i<=n;i++) {
// 题目要求存入数组,本题其实没必要,它只能检测结果,不能检测你代码
int tmp;
cin >> tmp;

if (isP(tmp)) cout << tmp << " ";
}

return 0;
}

二十一、字符与ASCII码

1. 字符

字符类型,即:char 类型C 风格字符串,可以存储字符或字符串的类型;

  • 字符类型说明:

    • 强调一下:char 类型 不是 string,string是声明的字符串,可以放一串字符;而char,只能放一个;

      • 例:char a = ‘A’ , 1. 只能存一个字符; 2. 必须是单引号包含;
    • 如何表示字符串?可以用字符数组来表示:

      • char a[5] = “hello”; 1. 这是错的,需要预留一位存储\0(空字符null)来表示结束 ;
      • char a[6] = “hello”; 2. 这是对的,此时可以用双引号来表示字符串;
      • cout << a; 3. 直接输出即可,无须数组式的循环逐个输出;
      a[0] a[1] a[2] a[3] a[4] a[5]
      h e l l o \0
      1
      2
      3
      4
      5
      6
      //char a = 'A';
      //cout << a;

      // 5位不够,需要预留一位存储\0(空字符null)来表示结束
      char a[6] = "hello";
      cout << a;
    • 初始化声明方法和普通数组差不多:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 初始化字符数组
      char a[6];
      // 初始化并直接赋值
      char b[6] = {"hello"};
      // 初始化并赋值初始值
      char c[6] = {0};

      // 动态初始
      char d[] = {"world"};

2. ASCII码

ASCII码,即:美国标准信息交换代码;用于和计算机数据通信;

  • 码表:

ASCII表

  • 通过上述表查阅到:小写a的十进制编码为97,然后逐步递增。大写的A是65,往后逐步递增;

  • 什么是十进制,ASCII码到底如何和计算机通信,解决了啥问题?自己百度查阅,我们只关注答题啊;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 字符型a
    char a = 'a';
    // 输出它的十进制,即整型数值
    cout << int(a);

    // 整型数值65
    int b = 65;
    // 输出它的字符
    cout << char(b);

习题四十六:1971.大小写转换

1971.大小写转换

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
char a;
cin >> a;

// 查表:a 97,A 65 梳理两点:
// 1. a跑到A,a - 32 = 65
// 2. A跑到a,A + 32 = 97

// 是否需要转换整数后判断?答:不需要
// 直接字符判断即可,它底层会自行转换判断
// 直接判断>='A',意味>=97,得到大写的范围
if (a >= 'A' && a <= 'Z') {
a += 32;
} else {
a -= 32;
}

// 输出a
cout << a;

习题四十七:P5733.自动修正

P5733.自动修正

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
char a[110] = {0};
cin >> a;

// 1. 小写变大写,其它不变
// 2. 如果本身是大写,则不变
// 3. 如果是数字和符号,则不变

// 怎么循环去判断是否要大写?
// 可以判断字符数组最后一位是'\0'结束
// cout << a[0]; 从0开始填入字符的
// 死循环
int cnt = 0;
while(1){
// 遇到结尾就退出
if (a[cnt] == '\0') break;
// 判断是小写
if (a[cnt] >= 'a' && a[cnt] <= 'z') a[cnt] -= 32;
// 累加
cnt++;
}

// 输出
cout << a;

二十二、cstring头和字符函数

1. sizeof操作符

字符数组是如何获取它的长度,比如占用多少,或者有多少个字符?之前的题目,都只是判断\0结束

  • sizeof:

    • 它可以返回一个字符数组所占的内存字节数(字节数理解为:数组分配的一个个小格子)

    • 比如 : char a[10] = “hello” , 它占用了多少?

      1
      2
      3
      4
      5
      6
      7
      // 占用10个字节,{"hello"} 和 {'h','e','l','l','o'}; 都一样
      char a[10] = "hello";
      cout << sizeof(a);

      // 占用6个字节,不定式,末尾\0占一个
      char a[] = "hello";
      cout << sizeof(a);
    • 所以,想用 sizeof 操作符 来 获取字符个数,其实并不方便。当然,用 a[] 这种 ,然后 - 1也可以。

2. cstring头

cstring是 C 标准库头文件, 包含了一些 C 风格字符串的类型和函数;

  • 基本使用:

    • 首先要引入 cstring : #include

      1
      2
      3
      #include <iostream>
      #include <cstring>
      using namespace std;
    • 使用 strlen() 函数 来获取 字符数组的长度:

      1
      2
      3
      4
      5
      // cstring下的strlen函数获取
      // 并不会统计占用,也不会统计结束符
      // 结果为:5
      char a[10] = "hello";
      cout << strlen(a);
    • 使用 strcpy()strncpy() 函数赋值字符数组:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      // 无法直接复制
      // char b[20] = a;
      // cout << b;

      // strcpy复制字符数组
      char b[20];
      strcpy(b, a);
      cout << b;

      // strncpy复制指定个数
      char c[20];
      strncpy(c, a, 3);
      cout << c;
    • 使用 strcat()strncat() 函数,连接字符数组:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      // 连接两个字符数组
      char a[20] = "hello";
      char b[10] = ", world!";
      // 函数本身会返回连接后的字符串
      // 参数1:被添加的字符数组;
      // 参数2:添加的字符数组;
      cout << strcat(a, b) << endl;
      // a被添加了,所以它增加了字符
      cout << a << endl;
      // b不变
      cout << b << endl;

      // 连接部分字符数组
      char a[20] = "hello";
      char b[10] = ", world!";
      cout << strncat(a, b, 5) << endl;
      cout << a << endl;
      cout << b << endl;
    • 使用 strchr()strstr() 函数,来实现字符数组查找:

      1
      2
      3
      4
      5
      6
      7
      // 查找字符在数组中的位置
      char a[20] = "hello, world!";
      // 获取第一次查询到的字符位置,并往后输出字符
      cout << strchr(a, 'o');
      // 获取到底是第几个位置
      cout << strchr(a, 'o') - a + 1;

      1
      2
      3
      4
      // 查询一串字符在数组中的位置
      char a[20] = "hello, world!";
      cout << strstr(a, "or");
      cout << strstr(a, "or") - a + 1;
    • 更多 cstring 旗下的函数:https://www.apiref.com/cpp-zh/cpp/header/cstring.html


二十三、string字符串.常用函数

1. string

string是标准模板库 STL 中提供处理字符串的工具类型,可以直接赋值使用,无须设置数组;

  • 字符串类型:

    • 可以直接赋值,无须数组化,但本身又是数组;

    • string是弹性长度,根据你赋值的内容自动伸缩,无须考虑赋值越界问题;

      1
      2
      3
      4
      5
      6
      7
      8
      // 字符串需双引号
      string s = "hello!";
      cout << s;

      // string本身也是数组
      cout << s[0];
      cout << s[3];
      cout << s[5];

2. 常用函数

字符串类型,有非常多的实用函数工具,这里把最最常用的罗列出来,后续课程逐步补充

  • 首先,字符串类型 string 本质是一个类(和之前的类型有所区别),它函数语法是类形式的语法:

    • 语法结构为:字符串变量.函数() ,这里的 函数(),我个人喜欢读成 方法();
    • 也就是:字符串变量.方法() ,在使用这些方法时,需要 include
    • 当然,我这里使用如下方法并没有引入 也可以使用,可见已被隐式引入;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    // 字符串
    string s = "hello";

    // 获取字符串长度size()或length()方法
    cout << s.size();
    cout << s.length();
    cout << s.max_size(); // 获取最大字符数

    // 拼接方法append(),在s后面添加字符串
    s.append(", world!");
    cout << s;

    // 拼接字符串,直接用+号也能实现
    s += ", world!";
    cout << s;

    // 插入字符(一个),push_back()末尾插入
    s.push_back('a'); // 一个字符单引号
    cout << s;

    // 指定位置插入字符或字符串:insert()
    s.insert(3, "@@@"); // 字符串形式,两个参数:位置和字符串
    cout << s;
    s.insert(3, 4, '#'); // 字符形式,三个参数:位置、个数、单个字符
    cout << s;

    // 查找字符串中是否包含某个字符:find()
    // 不存在返回:-1,存在返回:位置
    cout << s.find("ll"); // 只有一个参数时

    // 参数2,从第几个位置查找,自然找不到,会返回一个超大的数
    // 超大数不好判断没找到,一般是最好返回-1
    // 但由于find()本身返回的是无符号整数,即:没有负数,所以无法返回-1
    // 直接用 int 转换下即可
    cout << int(s.find("ll", 3)) << endl;
    // 或赋值方案
    int index = s.find("11", 3);
    cout << index;

    // 字符串截取:substr
    cout << s.substr(2); // 从第2个位置截取到最后
    cout << s.substr(2,2); // 从第2个位置截取2个

二十四、string.char.int的相互转换

1. char转int

其它现代语言数据类型转换都比较方便,而C++这种语言机制导致转换起来较为麻烦

  • char字符转int:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 先抛出问题:string数字转int,常规方法int转不了
    string x = "97";
    cout << x + 1; // 语法错误,无法相加
    cout << int(x) + 1; // 语法错误,无法转换


    // 字符 char转ascii码
    char y = 'a';
    cout << y; // 输出字符a
    cout << int(y); // 输出ascii码97,所以,用int转换char字符,只会得到ascii码

    // 字符数字 char转int
    char z = '8';
    cout << int(z); // 输出的是56,得到的是ascii码
    cout << z - '0'; // 输出的是8,可以得到整型8
    cout << z - '0' - 1; // 输出的是7,为了验证是不是int,减1便知
    cout << atoi(&z); // 输出的是8,&表示z变量的引用地址(指针),不用理解,记住语法即可

    // 关于指针和引用地址,会在后续课程中专门篇幅讲解(不是本套),目前都是有意避开,避不开的强记

2. string转int

将 string 转换成 int 的思路:就是先让 string 变成 char 的引用,然后用 atoi() 函数转换

  • string字符转int:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // string转int
    string k = "123";
    // 先转换到char的指针,使用c_str()函数就是这个意思
    // 然后再用atoi函数包裹转成int
    cout << atoi(k.c_str()) + 1; // 124

    // 当然还提供了直接转换的函数stoi
    string p = "456";
    cout << stoi(p);

二十五、读入一行.fgets.getline函数

1. getline()函数

在读入字符串时,遇到换行和空格,它是认为下一次 cin 读入,这给我们编码带来一些麻烦

  • getline()读入一整行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 先抛出问题,假设s1输入=> hello world!
    string s1;
    cin >> s1;
    cout << s1; // 结果为hello,空格后的字符串会理解为下一次cin

    // scanf也不行
    string s2;
    scanf("%s", &s2); // 直接报错,无法使用
    cout << s2;

    // scanf单字符输入
    char a;
    scanf("%c", &a); // 单字符输入,不管输入多少,得到1个字符
    cout << a;

    // scanf字符数组输入,字符数组本身是引用地址,无须&前缀
    char s[10];
    scanf("%s", s); // 输入多少,输出多少,可是遇到空格就没了
    cout << s;
    1
    2
    3
    4
    5
    // getline(cin, str)  参数1:固定cin,参数2:输入的字符串变量
    // getline()函数 适用于 string类型
    string s;
    getline(cin, s); // 输入 hello world! 不会被空格断掉
    cout << s;

2. fgets()函数

对于字符数组,我们可以采用 fgets() 函数读入一整行

  • fgets()读入一整行:

    1
    2
    3
    4
    5
    6
    7
    // 字符数组s
    char s[20];
    // 参数1:要存储的字符数组
    // 参数2:限定最大读入长度,防止越界
    // 参数3:固定参数,表示标准输入
    fgets(s, sizeof(s), stdin);
    cout << s;
  • cin.getline()读入一整行:

    1
    2
    3
    4
    // 和fgets()函数类似
    char s[20];
    cin.getline(s, sizeof(s));
    cout << s;

习题四十八:1478.出现次数最多的小写字母

1478.出现次数最多的小写字母

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
char s[110];
int a[130] = {0};
cin >> s;

// 循环输入的字符
// 获取长度,strlen返回的是size_t(无符号整型),用int转换
for(int i=0;i<=int(strlen(s));i++){
// 转换成ascii,移位到数组初始,并累计
a[int(s[i])]++;
}

// 假设a最大
int maxn = 97;
// 输出字母的ascii次数
for(int i=98;i<=122;i++){
// 比大小,注意这里必须有等于,方便将ascii码大的交给maxn
if (a[maxn] <= a[i]) maxn = i;
}

// 输出
cout << char(maxn);

习题四十九:P1125.NOIP2008.提高组.笨小猴

P1125.NOIP2008.提高组.笨小猴

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
char s[110];
int a[30] = {0}; // 从0开始存储a-z
cin >> s;

// 求出s长度
int len = strlen(s);

// 最大最小值
int maxn = -1, minn = len;

// 循环97-122
// 或者0-25,用当前字母的ascii-97或-'a'即可
// cout << 98 - 'a'; 相当于98 - 97
for(int i=0;i<=len-1;i++){
a[s[i] - 'a']++;
}

// 求最大值和最小值
for(int i=0;i<=25;i++){
if (a[i] != 0) {
maxn = max(maxn, a[i]);
minn = min(minn, a[i]);
}
}

// 判断质数
int d = maxn - minn;
// 特殊
if (d == 0 || d == 1) {
cout << "No Answer" << endl;
cout << 0;
return 0;
}

// 循环
for(int i=2;i<=sqrt(d);i++){
if (d % i == 0) {
cout << "No Answer" << endl;
cout << 0;
return 0;
}
}

// 输出结果
cout << "Lucky Word" << endl;
cout << maxn - minn;

习题五十:1134.国王的魔镜

1134.国王的魔镜

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 判断是否回文
bool isHW(char x[]) {
int len = strlen(x);

// 情况1:回文必须是偶数
if (len % 2 != 0) return false;

// 情况2:必须是回文
// 假设传过来的是: ABBA,判断03、12是否相等
// 假设传过来的是: ABBAABBA,判断07、16、25...是否相等
for(int i=0;i<=len/2-1;i++){
if (x[i] != x[len - 1 - i]) return false;
}
return true;
}


int main() {
char s[110];
cin >> s;

// 截取一半,如果用strcpy也行,但需要另外创建一个字符数组变量
// 此方法,给一半后面位置强行改为0,表示结束了
// s[len / 2] = '\0';
// cout << s;

// 截取后的字符数组
// 循环判断是不是回文:即倒过来也一样
while(isHW(s)){
s[strlen(s) / 2] = '\0';
}

// 输出最终长度
cout << strlen(s);

return 0;
}

习题五十一:P5015.NOIP2018.普及组.标题统计

P5015.NOIP2018.普及组.标题统计

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
string s;
getline(cin, s);

int cnt = 0;
// 统计
for(int i=0;i<=(int)s.length()-1;i++){
// string被拆分单字符,用单引号判断
if (s[i] != ' ') cnt++;
}

cout << cnt;

习题五十二:1012.我是第几个单词

1012.我是第几个单词

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
string s;
getline(cin, s);

string x;
getline(cin, x);

// 查找位置
int p = s.find(x);

// 判断
if (p == -1) {
// 总长度
int len = s.length();
// 去掉空格数
for(int i=0;i<=(int)s.length()-1;i++){
if (s[i] == ' ') len--;
}
// 去掉末尾点
cout << len - 1;
}
else {
int cnt = 0;
// 计算到p的空格数
for(int i=0;i<=p;i++){
if (s[i] == ' ') cnt++;
}
// 三个空格,就是第四个单词
cout << cnt + 1;
}

习题五十三:P1308.NOIP.普及组.统计单词数

P1308.NOIP.普及组.统计单词数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 转成小写
string to_lower(string x) {
// 先将字符全部转换成小写
for(int i=0;i<=(int)x.length();i++){
// 如果是大写字母,就转成小写
if (isupper(x[i])) {
x[i] = char(tolower(x[i]));
}
}

return x;
}

int main() {
string w, s;
getline(cin, w);
getline(cin, s);

// 处理大小写
w = to_lower(w);
s = to_lower(s);

// 单词而不是字符串,需要给单词前后加上空格
w = " " + w + " ";
s = " " + s + " ";

// 判断是否找到
int i = s.find(w);
// 找不到直接-1
if (i == -1) {
cout << -1;
} else {
int cnt = 0;
int f = i; // 第一次出现的位置
// 有多个单词,用while循环逐个找
while(i != -1){
cnt++; // 找到一个就+1
i = s.find(w, i + 1); // 在i位置找到,就+1位置往后继续找
}
cout << cnt << " " << f;
}

return 0;
}

习题五十四:1113.隐藏的最大整数

1113.隐藏的最大整数

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
string s;
cin >> s;

// 连续字符,初始空字符串
string num, maxn = "";
// 循环
for(int i=0;i<=(int)s.length()-1;i++){
// 判断是否是数字
if (isdigit(s[i])) {
num += s[i];
} else {
// 把连续的数字字符比较后赋值给maxn
if (num.length() > maxn.length()) maxn = num;
// 清空
num = "";
}

}

// 输出位置
cout << s.find(maxn) + 1;

二十六、结构体概念入门

1. 基本格式

结构体:即一组变量的聚合体,可以将非同类型的数据绑定成一体的这么个东西

  • 结构体语法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 结构体类型创建,并对内部变量直接赋初始值
struct info {
string name = "王二狗";
int age = 15;
char gender = 'A';
float weight = 1.55;
bool flag = true;
};

// 定义一个结构体变量:struct info a;
// 而struct可以省略,方便阅读,可以把info当成自定义类型
info a;

int main() {
// 调用结构体变量
cout << a.name << endl;
cout << a.age << endl;
cout << a.gender << endl;
cout << a.weight << endl;
cout << a.flag << endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 初始化交给变量声明
struct info {
char name[20];
int age;
char gender;
float weight;
bool flag;
};

// 可以初始化{}
info a = {};

// 初始化0,需要把string改为char name[20],否则报错
info a = {0};

// 对标赋值
info a = {"张三", 12, 'B', 1.38, true};

// 还有一种结合在一起的写法
struct info {
char name[20];
int age;
char gender;
float weight;
bool flag;
} a = {"张三", 12, 'B', 1.38, true};

2. 数组

结构体数组:如果要实现多组结构体数组,和普通数据使用方法一样,结合结构体语法即可

  • 数组语法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    struct grade {
    string name;
    int age;
    char gender;
    };

    int main() {
    // 不省略struct,提高可读性
    struct grade s[3] = {{"张三", 15, 'A'}, {"李四", 14, 'B'}, {"王五", 13, 'A'}};
    // 输出结构体数组第一条
    cout << s[0].name << " " << s[0].age << " " << s[0].gender;

    return 0;
    }

习题五十五:P5744.培训

P5744.培训

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 学员信息
struct info {
string name;
int age;
int score;
};

int main() {
// 初始化结构体
struct info stu[10];
int n;
cin >> n;

// 赋值
for(int i=1;i<=n;i++) cin >> stu[i].name >> stu[i].age >> stu[i].score;


// 计算
for(int i=1;i<=n;i++){
// 年龄+1岁
stu[i].age++;
// 分数+20%,但不大于600分
if (stu[i].score * 1.2 < 600) stu[i].score *= 1.2;
else stu[i].score = 600;
}

// 输出
for(int i=1;i<=n;i++){
cout << stu[i].name << " " << stu[i].age << " " << stu[i].score << endl;
}

return 0;
}

习题五十六:P5740.最厉害的学生

P5740.最厉害的学生

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct info {
char name[10];
int chi;
int mat;
int en;
};

int main() {
struct info stu[1010];
int n;
cin >> n;

// 赋值
for(int i=1;i<=n;i++)
cin >> stu[i].name >> stu[i].chi >> stu[i].mat >> stu[i].en;

// 求最大值
int maxn = -1, index = -1;
for(int i=1;i<=n;i++){
// 总分
int total = stu[i].chi + stu[i].mat + stu[i].en;
if (maxn < total) maxn = total, index = i;
}

// 输出最厉害
cout << stu[index].name << " " << stu[index].chi << " "
<< stu[index].mat << " " << stu[index].en;

return 0;
}

习题五十七:1953. 新生舞会

1953. 新生舞会

* 代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <cstring>
using namespace std;

// 结构体
struct info {
char name[20];
char uid[10];
char sex;
};

struct info stu[1010];
int n;

// 性别判断
char getSex(char str[], int mark) {
char sex;
for(int j=1;j<=n;j++){
char tmp[20];
if (mark == 0) strcpy(tmp, stu[j].uid);
else strcpy(tmp, stu[j].name);
// 对比所有uid或name
if (strcmp(str, tmp) == 0) {
sex = stu[j].sex;
}
}
return sex;
}

// isD
char isD(char s[]) {
if (isdigit(s[0])) return getSex(s, 0);
else return getSex(s, 1);
}


int main() {
cin >> n;

// 赋值
for(int i=1;i<=n;i++)
cin >> stu[i].name >> stu[i].uid >> stu[i].sex;

// 询问
int m;
cin >> m;
char s1[20], s2[20];

for(int i=1;i<=m;i++){
// 读入一组询问
cin >> s1 >> s2;

// 判断性别
if (isD(s1) == isD(s2)) cout << "N" << endl;
else cout << "Y" << endl;
}

return 0;
}