> 技术文档 > 讲解七道蓝桥杯省/国赛真题(第二弹)_小学蓝桥杯编程比赛样题

讲解七道蓝桥杯省/国赛真题(第二弹)_小学蓝桥杯编程比赛样题


讲解七道蓝桥杯省/国赛真题(第二弹)

  • 99 分巧克力
  • 题目介绍
  • 方法一:
    • 精彩代码模块
      • 在1e5个数据中快速找到符合条件的数据
    • 解题思路分析
  • 502 成绩统计
  • 题目介绍
  • 方法一:
    • 代码片段解释
      • 片段一:
      • 片段二:
    • 精彩代码模块
      • 对一个小数进行四舍五入保留整数
  • 126 交换瓶子
  • 题目介绍
  • 方法一:
    • 核心原理演示
    • 代码片段解释
      • 片段一:
    • 精彩代码模块
      • 从下标为1的位置开始为一维数组赋值
      • 将一维数组中的元素排序为元素的值与下标的值相同
  • 130 移动距离
  • 题目介绍
  • 方法一:
    • 代码片段解释
      • 片段一:
    • 精彩代码模块
      • 元素是二维数组的第几个元素 + 二维数组的列数 -> 该元素在二维数组的第几行
      • 为二维数组进行S形赋值
    • 解题思路分析
  • 141 穿越雷区
  • 题目介绍
  • 方法一:
    • 解题思路分析
  • 549 扫雷
  • 题目介绍
  • 方法一:
    • 精彩判断解释
      • 遍历二维数组中的每个元素并有针对性的遍历某个元素周围的元素
    • 解题思路分析
  • 方法二:
    • 解题思路分析
  • 505 数字三角形
  • 题目介绍
  • 方法一:
    • 代码片段解释
      • 片段一:
    • 精彩代码片段
      • 对二位数组进行直角三角行赋值
      • 对于一行有奇数或偶数个元素怎么找到这行元素的中间的元素

99 分巧克力

题目介绍

开始挑战:99 分巧克力

在这里插入图片描述

方法一:

#include #include using namespace std;int chocolateCount; // 巧克力的块数int childCount; // 小朋友的数量// 判断使用指定边长分割巧克力是否能满足小朋友的数量需求bool canSatisfyChildren(int sideLength, vector<vector<int>>& chocolates){// 统计使用当前边长能分割出的巧克力总块数int totalPieces = 0;//切记这里的totalPieces一定要定义为局部变量,应为之后函数还会被调用,其值不能累加// 遍历每一块巧克力for (int i = 0; i < chocolateCount; i++){// 计算当前巧克力使用指定边长能分割出的块数totalPieces += (chocolates[i][0] / sideLength) * (chocolates[i][1] / sideLength);// 判断分割出的总块数是否大于等于小朋友的数量return totalPieces >= childCount;}}int main(){int length, width;// 输入巧克力的块数和小朋友的数量cin >> chocolateCount >> childCount;// 存储每块巧克力的长和宽信息vector<vector<int>> chocolates(chocolateCount, vector<int>(2));// 输入每块巧克力的长和宽for (int i = 0; i < chocolateCount; i++){cin >> length >> width;chocolates[i][0] = length;chocolates[i][1] = width;}// 记录能满足条件的最大分割边长int maxSideLength = 1;// 二分查找的左边界,最小边长为 1int left = 1;// 二分查找的右边界,最大边长为 100000int right = 1e5;// 二分查找过程while (left <= right){// 计算中间边长int mid = (left + right) / 2;// 判断中间边长是否能满足小朋友的数量需求if (canSatisfyChildren(mid, chocolates)){// 如果满足,记录该边长并尝试更大的边长maxSideLength = mid;left = mid + 1;}else{// 如果不满足,尝试更小的边长right = mid - 1;}}// 输出能满足条件的最大分割边长cout << maxSideLength;return 0;}

精彩代码模块

在1e5个数据中快速找到符合条件的数据

// 记录能满足条件的最大分割边长int maxSideLength = 1;// 二分查找的左边界,最小边长为 1int left = 1;// 二分查找的右边界,最大边长为 100000int right = 1e5;// 二分查找过程while (left <= right){// 计算中间边长int mid = (left + right) / 2;// 判断中间边长是否能满足小朋友的数量需求if (canSatisfyChildren(mid, chocolates)){// 如果满足,记录该边长并尝试更大的边长maxSideLength = mid;left = mid + 1;}else{// 如果不满足,尝试更小的边长right = mid - 1;}}

解题思路分析

要求:

  1. 分割出的巧克力为正方形
  2. 每块巧克力采用相同的边长进行分割 —> 分割出的巧克力大小相同
  3. 分割出的巧克力尽可能的大
  4. 分割出的巧克力的数量 >= 小朋友的数量

思路:

分割出的巧克力的数量 >= 小朋友的数量

  • 快速找到合适的分割巧克力的分割长度 —> 使用二分查找查找合适的分割长度。
  • 累加每块巧克力使用该分割长度分的巧克力的块数
    • 我们要怎么计算一块巧克力可以分的巧克力?
  • 判断分割出的巧克力的数量与小朋友的数量的关系

分割出的巧克力尽可能的大

  • 增大分割长度,看看还能不能满足条件

分割出的巧克力为正方形 + 分割出的巧克力大小相同 ===》怎么计算一块巧克力可以分的巧克力?

// 遍历每一块巧克力for (int i = 0; i < chocolateCount; i++){ // 计算当前巧克力使用指定边长能分割出的块数 totalPieces += (chocolates[i][0] / sideLength) * (chocolates[i][1] / sideLength); // 判断分割出的总块数是否大于等于小朋友的数量 return totalPieces >= childCount;}

502 成绩统计

题目介绍

开始挑战:502 成绩统计

在这里插入图片描述

方法一:

#include using namespace std;int main(){ int score; int totalStudents, excellentCount = 0, outstandingCount = 0; cin >> totalStudents; // 统计成绩大于60分和大于85分的学生人数 for (int i = 0; i < totalStudents; i++) { cin >> score; if (score > 60) { excellentCount++; } if (score > 85) { outstandingCount++; } } // 计算百分比并四舍五入 int excellentPercentage = excellentCount * 100.0 / totalStudents + 0.5; int outstandingPercentage = outstandingCount * 100.0 / totalStudents + 0.5; // 输出结果 printf(\"%d%%\\n%d%%\\n\", excellentPercentage, outstandingPercentage); return 0;}

代码片段解释

片段一:

// 计算百分比并四舍五入int excellentPercentage = excellentCount * 100.0 / totalStudents + 0.5;int outstandingPercentage = outstandingCount * 100.0 / totalStudents + 0.5;

这段代码的作用是 计算百分比并四舍五入


1. 计算百分比

公式

t e x t 百分比 = ( 符合条件的数量 总数 ) × 100 text{百分比} = \\left( \\frac{\\text{符合条件的数量}}{\\text{总数}} \\right) \\times 100 text百分比=(总数符合条件的数量)×100

代码实现

excellentCount * 100.0 / totalStudents
  • excellentCount:是符合条件的数量(如:成绩大于60分的学生人数)
  • totalStudents:是学生总数
  • 100.0:是为了将结果转换为百分比

使用 100.0 而不是 100 是为了确保进行浮点数除法,避免整数除法丢失小数部分。


2. 四舍五入

问题

  • 运气好的话我们遇到的 及格率/优秀率 是四舍的情况,即使我们不进行四舍,double类型的数据在转换为int类型时发生截断的结果正好就是四舍的情况。

    • 如果 excellentCount = 3totalStudents = 9,计算结果为 33.333...,我们希望四舍五入为 33,正好截断的结果就是33
  • 运气差的话我们遇到的 及格率/优秀率 是五入的情况,如果此时我们不进行五入,double类型的数据在转换为int类型时发生截断的结果与五入的结果就完全不同了。

    • 如果计算结果为 33.666...,我们希望四舍五入为 34,而单纯截断的结果是33

    解决方法:在计算结果后加上 0.5,然后转换为整数。

  • 如果计算结果为 33.333...,加上 0.5 后为 33.833...,转换为整数后为 33

  • 如果计算结果为 33.666...,加上 0.5 后为 34.166...,转换为整数后为 34

代码实现:对一个小数进行四舍五入保留整数

excellentCount * 100.0 / totalStudents + 0.5
  1. 加上 0.5 是为了实现四舍五入。

  2. 转换为 int 类型后,小数部分会被截断,从而实现四舍五入。

片段二:

// 输出结果printf(\"%d%%\\n%d%%\\n\", excellentPercentage, outstandingPercentage);

疑问:为什么输出%需要写 %%

在C语言中,% 是一个特殊字符,用于格式化输出(如: %d 表示输出整数,%f 表示输出浮点数等)

因此:如果你想在 printf 中输出一个普通的 % 符号,而不是作为格式化占位符使用,你需要使用 %%

//如果你直接写%,例如:printf(\"%d%\\n%d%\\n\", excellentPercentage, outstandingPercentage);
  • 要是直接在格式化字符串里写一个 % 符号,printf 函数会把它当作格式说明符的起始标志,进而去寻找后续的字符来确定具体的格式说明符。
  • 但后面没有有效的格式化字符(如 df 等),从而导致编译错误或运行时错误。

所以:为了输出一个普通的 % 符号,就需要使用 %%,在 printf 函数的格式化字符串中,%% 会被解析为一个普通的 % 符号进行输出。


当然如果你觉得使用C语言输出%太繁琐的话,也可以使用C++的语法进行输出

cout<<excellentPercentage<<\'%\'<<outstandingPercentage<<n22<<\'%\';

精彩代码模块

对一个小数进行四舍五入保留整数

// 计算百分比并四舍五入int excellentPercentage = excellentCount * 100.0 / totalStudents + 0.5;int outstandingPercentage = outstandingCount * 100.0 / totalStudents + 0.5;

126 交换瓶子

题目介绍

开始挑战:126 交换瓶子

在这里插入图片描述

方法一:

#include #include using namespace std;int main(){ // 输入瓶子的数量 int bottleCount; cin >> bottleCount; // 定义瓶子的位置数组,大小为 bottleCount + 1(从1开始编号) vector<int> bottlePositions(bottleCount + 1); // 输入每个瓶子的初始位置 for (int i = 1; i <= bottleCount; i++) { cin >> bottlePositions[i]; } int swapCount = 0; // 记录交换次数 // 遍历每个瓶子 for (int i = 1; i <= bottleCount; i++) { // 如果当前瓶子的位置与其编号不一致,则进行交换 while (i != bottlePositions[i]) { // 将当前瓶子与其正确位置的瓶子交换 ---> 确保每次的交换都是有效的交换 swap(bottlePositions[i], bottlePositions[bottlePositions[i]]); swapCount++; } } cout << swapCount; return 0;}

核心原理演示

在这里插入图片描述

代码片段解释

片段一:

// 定义瓶子的位置数组,大小为 bottleCount + 1(从1开始编号)vector<int> bottlePositions(bottleCount + 1);// 输入每个瓶子的初始位置for (int i = 1; i <= bottleCount; i++){ cin >> bottlePositions[i];}

上面的代码片段做了两件比较特殊的事情:

1. 精确分配内存空间的定义了vector数组

为不是:

  • 不精确分配内存空间的定义了vector数组

    vector<int> bottlePositions(1e4);

    导致:对于实际瓶子数量很少的情况造成了空间浪费。

  • 不分配内存空间的定义了vector数组

    vector<int> bottlePositions;

    导致:后续为瓶子赋值要进行动态扩展。


2. vector数组多分配一个int空间

好处:数组从下标为1的位置开始存放数据也可以存够bottleCount个瓶子,那么数组的下标 i 可以作为瓶子所在的位置。


内心的疑问:如果我们从下标为1的位置赋值,那么下标为0的位置的值是什么呢?

代码示例

#include #include using namespace std;//代码测试的任务:为一个一维数组从下标1~n进行赋值,看看这个数组下标为0的位置存的是什么int main(){int num;cin >> num;//一维数组的定义vector<int>score(num+1);//一维数组的赋值for (int i = 1; i <= num; i++){cin >> score[i];}//一维数组的打印for (int i = 0; i < num; i++){cout << score[i] << \" \";}return 0;}

输出

3
60 70 80
0 60 70

解释:为什么 score[0]0

  • vector 的构造函数
    • vector score(num + 1); 调用了 vector 的构造函数,该构造函数会:
      1. 分配 num + 1 个元素的内存空间
      2. 对每个元素进行 值初始化(value-initialization)
        • 对于 int 类型,值初始化的结果是 0
        • 因此,score[0] 被默认初始化为 0,即使你没有显式赋值。
  • 值初始化的规则
    • 如果元素是 内置类型(如:intdouble 等),值初始化会将其设置为 0
    • 如果元素是 类类型,值初始化会调用默认构造函数

精彩代码模块

从下标为1的位置开始为一维数组赋值

// 定义瓶子的位置数组,大小为 bottleCount + 1(从1开始编号)vector<int> bottlePositions(bottleCount + 1);// 输入每个瓶子的初始位置for (int i = 1; i <= bottleCount; i++){ cin >> bottlePositions[i];}

将一维数组中的元素排序为元素的值与下标的值相同

// 遍历每个瓶子for (int i = 1; i <= bottleCount; i++){ // 如果当前瓶子的位置与其编号不一致,则进行交换 while (i != bottlePositions[i]) { // 将当前瓶子与其正确位置的瓶子交换 swap(bottlePositions[i], bottlePositions[bottlePositions[i]]); }}

130 移动距离

题目介绍

开始挑战:130 移动距离

在这里插入图片描述

方法一:

#include #include using namespace std;// 计算两个楼房之间的最短移动距离int calculateShortestDistance(int width, int buildingM, int buildingN) { int totalBuildings = (buildingN + width - 1) / width; vector<vector<int>> buildingCoordinates(totalBuildings, vector<int>(width)); int currentBuildingNumber = 1; // 用于给楼房编号的计数器 // 根据给定的宽度生成楼房编号矩阵 //这么模拟S形编号?--> 偶数行从左往右编号;奇数行从右往左进行编号(从第0行开始) for (int i = 0; i < totalBuildings; i++) { if (i % 2 == 0) //偶数行:左--->右 { for (int j = 0; j < width; j++) { buildingCoordinates[i][j] = currentBuildingNumber++; } } else { for (int j = width - 1; j >= 0; j--) { buildingCoordinates[i][j] = currentBuildingNumber++; } } } // 找到两个楼房的坐标 int x1, y1, x2, y2; for (int i = 0; i < totalBuildings; i++) { for (int j = 0; j < width; j++) { if (buildingCoordinates[i][j] == buildingM) { x1 = i, y1 = j; } if (buildingCoordinates[i][j] == buildingN) { x2 = i, y2 = j; } } } // 计算曼哈顿距离(最短移动距离) int shortestDistance = abs(y2 - y1) + abs(x2 - x1); return shortestDistance;}int main() { int width, buildingM, buildingN; scanf(\"%d %d %d\", &width, &buildingM, &buildingN); // 输出两个楼房之间的最短移动距离 printf(\"%d\", calculateShortestDistance(width, buildingM, buildingN)); return 0;}

代码片段解释

片段一:

int totalBuildings = (buildingN + width - 1) / width;
  • buildingN:是我们要计算的最大的楼房编号。(这道题默认n号楼的编号最大)
  • width:是每一行楼房的数量。
  • buildingN + width - 1:是为了容纳从 1buildingN 的所有楼房至少需要的行数。

总结:这行代码的目的是计算在给定的排号宽度 width 下,需要多少行才能容纳编号直到 buildingN 的所有楼房。


计算原理

  1. 常规除法的问题
  • 如果直接使用 buildingN / width 进行计算,当 buildingN 不能被 width 整除时,会 向下取整,忽略掉剩余的建筑物。

    • 例如,若 buildingN = 7width = 37 / 3 的结果是 2,但实际上 7 个建筑物每行放 3 个,需要 3 行才能全部放下。
  1. width - 1 的作用
  • 通过 (buildingN + width - 1) / width 这种计算方式,可以实现**向上取整**的效果。

buildingN = k * width + r,其中 k 是商,r 是余数(0 <= r < width

  • r = 0 时,即 buildingN 能被 width 整除。
    • (buildingN + width - 1) / width = (k * width + width - 1) / width,因为 width - 1 < width,所以结果就是 k,也就是刚好需要 k 行。
  • r > 0 时,即 buildingN 不能被 width 整除。
    • (buildingN + width - 1) / width = (k * width + r + width - 1) / width,由于 r + width - 1 >= width,所以结果是 k + 1,也就是需要 k + 1 行才能放下所有建筑物。

精彩代码模块

元素是二维数组的第几个元素 + 二维数组的列数 -> 该元素在二维数组的第几行

int totalBuildings = (buildingN + width - 1) / width;

为二维数组进行S形赋值

//这么模拟S形编号?--> 偶数行从左往右编号;奇数行从右往左进行编号(从第0行开始)for (int i = 0; i < totalBuildings; i++){ if (i % 2 == 0) //偶数行:左--->右 { for (int j = 0; j < width; j++) { buildingCoordinates[i][j] = currentBuildingNumber++; } } else { for (int j = width - 1; j >= 0; j--) { buildingCoordinates[i][j] = currentBuildingNumber++; } }}

解题思路分析

计算两个楼之间的最短距离且不能走斜线 —>只能“横着走”和或“竖着走” —>如果我们知道这两个楼的坐标

所以两个楼之间的最短距离 == abs(x1-x2) + abs(y1-y2)

难点:怎么找到这两个楼的坐标?

  1. 使用二维数组模拟出这个小区
    • 定义二维数组
      • 二维数组的行数多大合适 —> 怎么找到最大楼行所在的行?
    • 二维数组进行S形赋值
  2. 在二维数组中查找这两栋楼的坐标

141 穿越雷区

题目介绍

开始挑战:141 穿越雷区

在这里插入图片描述

方法一:

#include#include using namespace std;vector<vector<char>> grid(100, vector<char>(100)); // 存储网格地图vector<vector<bool>> visited(100, vector<bool>(100, false)); // 记录是否访问过某个位置//顺时针搜索:右、下、左、上int rowDirection[4] = { 0, 1, 0 , -1 };int colDirection[4] = { 1, 0, -1, 0 };int gridSize; // 网格的大小// 深度优先搜索函数,计算从(startRow, startCol)到目标位置的最短距离int dfs(int startRow, int startCol){visited[startRow][startCol] = true; // 标记当前位置为已访问// 如果当前位置是目标位置 \'B\',返回距离 0if (grid[startRow][startCol] == \'B\'){return 0;}int minDistance = gridSize * gridSize; // 初始化最小距离为一个较大的值// 遍历四个方向for (int i = 0; i < 4; i++){int newRow = rowDirection[i] + startRow;int newCol = colDirection[i] + startCol;// 如果新位置超出边界或已经访问过,跳过if (visited[newRow][newCol] || newRow > gridSize || newCol > gridSize || newRow < 1 || newCol < 1){continue;}// 如果新位置的字符与当前位置不同,递归搜索if (grid[startRow][startCol] != grid[newRow][newCol]){int currentDistance = dfs(newRow, newCol) + 1;minDistance = min(minDistance, currentDistance);visited[newRow][newCol] = false; // 回溯时取消标记}}return minDistance;}int main(){int startRow, startCol;cin >> gridSize;// 读取网格地图并找到起点 \'A\' 的位置for (int i = 1; i <= gridSize; i++){for (int j = 1; j <= gridSize; j++){cin >> grid[i][j];if (grid[i][j] == \'A\'){startRow = i;startCol = j;}}}int distance = dfs(startRow, startCol);// 如果距离等于网格大小平方,说明没有找到路径,输出 -1if (distance == gridSize * gridSize){cout << -1;}else{cout << distance;}return 0;}

解题思路分析

深度优先搜索的思路(dfs)

深搜的调用

  1. 准备调用数据

    • 一个存储矩阵信息的二维数组

    • 一个存储访问路径的二维数组

    • 两个存储出发点在矩阵中位置的横纵坐标的普通变量

    • 两个存储方向增量元素个数为4/8的一维数组

  2. 调用深搜函数

    • 参数:现在在矩阵中位置的横坐标和纵坐标

深搜函数的内部

第一步:标记现在所处的位置已被访问

第二步:使用if分支判断正常函数结束的情况

第三步:使用for循环4/8个方向增量

  • 第四步:更新新位置的横坐标和纵坐标

  • 第五步:剔除“已访问 + 越界”的新位置

  • 第六步:使用if分支判断是否满足题目的要求

    • 第七步:再次调用深搜函数进行递归
    • 第八步:完成题目中的任务
    • 第九步:回溯时取消访问记录

第十步:返回每次递归想要得到的东西

549 扫雷

题目介绍

开始挑战:549 扫雷

在这里插入图片描述

方法一:

#include using namespace std;int rows, cols; // 定义行数和列数int grid[100][100] = { 0 }; // 定义地图网格,初始化为0int main() { // 输入行数和列数 cin >> rows >> cols; // 输入地图信息 for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { cin >> grid[i][j]; } } // 处理地图并输出结果 for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (grid[i][j] == 1) { // 如果是地雷,输出9 cout << 9 << \" \"; } else { // 如果不是地雷,计算周围地雷的数量 int mineCount = 0; for (int x = i - 1; x <= i + 1; x++)  {  for (int y = j - 1; y <= j + 1; y++)  { // 检查边界条件,并统计地雷数量 if (x >= 0 && y >= 0 && x < rows && y < cols && grid[x][y] == 1) { mineCount++; }  } } // 输出周围地雷的数量 cout << mineCount << \" \"; } } // 换行 cout << endl; } return 0;}

精彩判断解释

遍历二维数组中的每个元素并有针对性的遍历某个元素周围的元素

// 处理地图并输出结果for (int i = 0; i < rows; i++){ for (int j = 0; j < cols; j++) { if (grid[i][j] == 1) { // 如果是地雷,输出9 cout << 9 << \" \"; } else { // 如果不是地雷,计算周围地雷的数量 int mineCount = 0; for (int x = i - 1; x <= i + 1; x++) { for (int y = j - 1; y <= j + 1; y++) {  // 检查边界条件,并统计地雷数量  if (x >= 0 && y >= 0 && x < rows && y < cols && grid[x][y] == 1)  { mineCount++;  } } } // 输出周围地雷的数量 cout << mineCount << \" \"; } } // 换行 cout << endl;}

解题思路分析

使用双重for循环来遍历雷区中的每一个位置

  1. 如果我门碰到了有雷的位置 —> 即是标记值为1的位置
    • 输出9
  2. 如果我们碰到了没雷的位置 —> 即是标记值为0的位置
    • 第一步:定义地雷数量计数器
    • 第二步:在以该位置为中心的3*3的雷区矩阵中使用双重for循环来遍历该矩阵
      • 使用if选择语句检查遍历到的这个位置是否“不越界 || 有地雷”

方法二:

#include #include using namespace std;int row, col;vector<vector<int>> grid(100, vector<int>(100));// 八个方向的移动增量:上、右上、右、右下、下、左下、左、左上(顺时针)int x[8] = { -1, -1, 0, 1, 1, 1, 0, -1 };int y[8] = { 0, 1, 1, 1, 0, -1, -1, -1 };int bfs(int currrow, int currcol){int count = 0;for (int i = 0; i < 8; i++){int newrow = currrow + x[i];int newcol = currcol + y[i];if (newrow < 1 || newrow > row || newcol < 1 || newcol > col){continue;}if (grid[newrow][newcol] == 9){count++;}}return count;}int main(){cin >> row >> col;for (int i = 1; i <= row; i++){for (int j = 1; j <= col; j++){cin >> grid[i][j];}}//bfs函数实现的功能是:将没有地雷的位置的值“由0变为其周围地雷的数量”//所以:对于雷区矩阵中的没有地雷位置都要调用bfs,来获得其周围地雷的数量for (int i = 1; i <= row; i++)//注意:一定要先把有地雷位置改为9 {for (int j = 1; j <= col; j++){if (grid[i][j] == 1){grid[i][j] = 9;//这里将其改为9:一方面是题目的要求,另一方面是因为防止其歧义为:是没有地雷的位置周边的地雷的数量为6(例如修改为了6)//毕竟没有地雷的位置周边的地雷的数量最大值为8}}}for (int i = 1; i <= row; i++)//注意:然后再去修改没有地雷位置的值{for (int j = 1; j <= col; j++){if (grid[i][j] == 0){grid[i][j] = bfs(i, j);}//下面的注释地方是之前的错误尝试://第一次的尝试://else//{ //grid[i][j]=9; //不要在这里修改有地雷位置的值,因为这样会影响后续判别雷的个数 //所以我们选择在没有地雷位置修改完之后再修改雷区的值//}cout << grid[i][j] << \" \";}putchar(\'\\n\');}//第二次的尝试://for(int i=1;i<=row;i++)//{ //for(int j=1;j<=col;j++) //{//if(grid[i][j]==1)//{ //grid[i][j]=9;//}//cout<<grid[i][j]<<\" \"; //} //putchar(\'\\n\');//}//但是又有新问题了;//问题1.如果先只处理没有地雷的位置的话:附近地雷的数量为1时 ---》该没有地雷的位置又被认为是有地雷的位置了,影响后续没有地雷位置周围地雷数量的判断//问题2.如果先只处理没有地雷的位置的话:附近地雷的数量为1时 ---》该没有地雷的位置又被认为是有地雷的位置了,最终没有地雷位置的值变成了9return 0;}

解题思路分析

方法二与方法一的核心的区别是:

  • 方法一:并不会对原来存放雷区矩阵信息的二维数组进行修改。
  • 方法二:对原来存放雷区矩阵信息的二维数组进行了修改。

具体思路步骤:

先使用双重for循环遍历雷区中的每一个位置,再使用if选择将雷区矩阵中有地雷的位置的值修改为9

再使用双重for循环遍历雷区中的每一个位置,再使用if选择将雷区矩阵中没有地雷的位置的值修改为其周围地雷的数量

怎么实现将雷区矩阵中没有地雷的位置的值修改为其周围地雷的数量? —> 调用bfs函数,函数返回其周围地雷的数量

  • 第一步:定义地雷数量计数器
  • 第二步:使用for循环8个方向增量
    • 更新新位置的横坐标和纵坐标
    • 剔除“越界”的新位置
    • 使用if选择语句判断新位置的值

505 数字三角形

题目介绍

开始挑战:505 数字三角形

在这里插入图片描述

方法一:

#include #include using namespace std;int main01() { int triangleSize; // 三角形的大小(行数) cin >> triangleSize; vector<vector<int>> triangle(triangleSize + 1, vector<int>(triangleSize + 1, 0)); // 存储输入的三角形数据 vector<vector<int>> dp(triangleSize + 1, vector<int>(triangleSize + 1, 0)); // 存储动态规划的结果 // 输入三角形的数据 for (int row = 1; row <= triangleSize; row++) { for (int col = 1; col <= row; col++) { cin >> triangle[row][col]; } } // 初始化动态规划的第一行 dp[1][1] = triangle[1][1]; // 动态规划计算每一行的最大值 for (int row = 2; row <= triangleSize; row++) { for (int col = 1; col <= row; col++) { dp[row][col] = triangle[row][col] + max(dp[row - 1][col - 1], dp[row - 1][col]); } } // 输出最后一行的中间两个位置的最大值 ---> 如果是“奇数”那么mid1 和 mid2的计算机结果一样; int mid1 = (triangleSize + 1) / 2; //如果是“偶数”那么mid1 和 mid2的计算机结果正好是最后一层中间的那两个数字 int mid2 = (triangleSize + 2) / 2; cout << max(dp[triangleSize][mid1], dp[triangleSize][mid2]) << endl; return 0;}

代码片段解释

片段一:

vector<vector<int>> triangle(triangleSize + 1, vector<int>(triangleSize + 1, 0)); vector<vector<int>> dp(triangleSize + 1, vector<int>(triangleSize + 1, 0));

疑问:该二维数组的列数每行都在变化,也即是其列我们是无法判断,那我们为什么还要定义时说明有几列?

在 C++ 中,如果你使用 vector 来定义二维数组,必须为每一行分配列数,否则无法直接通过索引访问赋值

这是因为 vector 是一个动态数组,它的每一行都是一个独立的 vector,如果没有初始化列数,访问时会引发未定义行为或崩溃。


代码示例:在不给二维数组定义列的情况下是否还可以为二维数组进行赋值?

#include #include using namespace std;int main(){ int num; cin >> num; //vector<vector> triangle(101); //error //代码测试:在不给二维数组定义列的情况下是否还可以为二维数组进行赋值 vector<vector<int>> triangle(101,vector<int>(101,0)); for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { cin >> triangle[i][j]; } } for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { cout << triangle[i][j] << \" \"; } putchar(\'\\n\'); }}

vector<vector> triangle(101); 这里只初始化了 triangle 的行数为 101,但每一行的列数未初始化。

因此:当你尝试访问 triangle[i][j] 时,程序会崩溃,因为 triangle[i] 是一个空的vector,没有分配任何内存。

精彩代码片段

对二位数组进行直角三角行赋值

// 输入三角形的数据for (int row = 1; row <= triangleSize; row++){ for (int col = 1; col <= row; col++) { cin >> triangle[row][col]; }}

对于一行有奇数或偶数个元素怎么找到这行元素的中间的元素

// 如果是“奇数”那么mid1 和 mid2的计算机结果一样;如果是“偶数”那么mid1 和 mid2的计算机结果正好是最后一层中间的那两个数字int mid1 = (triangleSize + 1) / 2; int mid2 = (triangleSize + 2) / 2;