洛谷P2670扫雷游戏(Java)

发布于:2024-12-06 ⋅ 阅读:(108) ⋅ 点赞:(0)

三.P2670 [NOIP2015 普及组] 扫雷游戏

题目背景

NOIP2015 普及组 T2

题目描述

扫雷游戏是一款十分经典的单机小游戏。在 n 行 m列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。游戏的目标是在不翻出任何地雷格的条件下,找出所有的非地雷格。

现在给出 n行 m列的雷区中的地雷分布,要求计算出每个非地雷格周围的地雷格数。

注:一个格子的周围格子包括其上、下、左、右、左上、右上、左下、右下八个方向上与之直接相邻的格子。

输入格式

第一行是用一个空格隔开的两个整数 n和 m,分别表示雷区的行数和列数。

接下来 n 行,每行 m个字符,描述了雷区中的地雷分布情况。字符 * 表示相应格子是地雷格,字符 ? 表示相应格子是非地雷格。相邻字符之间无分隔符。

输出格式

输出文件包含 n行,每行 m 个字符,描述整个雷区。用 *表示地雷格,用周围的地雷个数表示非地雷格。相邻字符之间无分隔符。

样例 #1

样例输入 #1

3 3
*??
???
?*?

样例输出 #1

*10
221
1*1

样例 #2

样例输入 #2

2 3
?*?
*??

样例输出 #2

2*1
*21

算法思路

输出的结果需要地雷(也就是*原样输出),而?地方则输出地雷数。中间的数算 上、下、左、右、左上、右上、左下、右下八个方向 的地雷数当然好算,但是边界的数该如何解决呢?

我的思路是将输入的原数组周围在套上一层。我这边是都补的字符 0 (自己画的,理解就好),那么三行三列的数组就变成了五行五列,同样的两行三列变成了四行五列。总结:就是将行和列都 +2 就可以了。这样边界问题就可以很轻松的解决了。每个格子都有八个方向了。当遇到 0 时直接不管就行。

image-20241201223950076
// 为了加上虚拟边界,创建大小为 (n+2) x (m+2) 的 ch 数组
char[][] ch = new char[n + 2][m + 2]; // 边界填充的数组
char[][] res = new char[n][m]; // 存储计算结果的数组

读取输入数据,并填充到 ch[1] 到 ch[n] 中

//这里不能这样写,困扰我好久,一直报ArrayIndexOutOfBoundsException,原因是toCharArray方法将字符串转化为字符数组时是从下标为0开始的,而不是1。
        for (int i = 1; i <= n; i++) {
            String line = scan.nextLine();
            ch[i] = line.toCharArray();
        }

首先我们来说明下toCharArray

toCharArray() 是 Java 中 String 类的一个方法,用于将字符串转换为字符数组(char[])。它的返回值是一个包含字符串中所有字符的数组,每个字符都被存储在数组的相应位置。

当自动字符串转字符时是从下标0开始的,但是我们的下标为0 的是虚拟数组的边界。这样就导致有个位置的数组是空的。

ArrayIndexOutOfBoundsException 是 Java 中的一种运行时异常,表示数组下标越界异常。当你尝试访问数组中不存在的索引时,就会抛出这个异常。

如图,就会有三个位置为null,导致异常。

image-20241201235337491
//改成逐一赋值,就没问题了。
        for (int i = 1; i <= n; i++) {  // 从 1 到 n 填充数据
            String line = scan.nextLine(); // 读取输入的一行
            for (int j = 0; j < m; j++) {
                ch[i][j + 1] = line.charAt(j);  // 将字符逐个赋值到 ch[i][j+1]
            }
        }

默默吐槽一句,java真不适合写算法。

代码

import java.util.Arrays;
import java.util.Scanner;

public class P2670 {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt(); // 行数
        int m = scan.nextInt(); // 列数
        scan.nextLine();  // 消耗掉换行符

        // 为了加上虚拟边界,创建大小为 (n+2) x (m+2) 的 ch 数组
        char[][] ch = new char[n + 2][m + 2]; // 边界填充的数组
        char[][] res = new char[n][m]; // 存储计算结果的数组

        // 初始化二维数组,将所有元素填充为 '0'
        for (int i = 0; i < n + 2; i++) {
            Arrays.fill(ch[i], '0');
//            也可以写两个循环
//            for (int j = 0; j < n + 2; j++) {
//                ch[i][j] = '0';
//            }

        }

        // 读取输入数据,并填充到 ch[1] 到 ch[n] 中
        for (int i = 1; i <= n; i++) {  // 从 1 到 n 填充数据
            String line = scan.nextLine(); // 读取输入的一行
            for (int j = 0; j < m; j++) {
                ch[i][j + 1] = line.charAt(j);  // 将字符逐个赋值到 ch[i][j+1]
            }
        }

        // 计算每个位置周围的星号数
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                int count = 0;
                if (ch[i][j] == '*') {
                    res[i - 1][j - 1] = '*'; // 如果当前位置是星号,直接赋值
                } else {
                    // 遍历当前位置周围的 3x3 区域
                    for (int k = i - 1; k <= i + 1; k++) {
                        for (int l = j - 1; l <= j + 1; l++) {
                            if (ch[k][l] == '*') {
                                count++; // 计算周围星号的数量
                            }
                        }
                    }
                    res[i - 1][j - 1] = (char) (count + '0'); // 将数量转换为字符并存储
                }
            }
        }

        // 输出结果
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                System.out.print(res[i][j]); // 打印每个字符,不换行
            }
            System.out.println(); // 每行输出后换行
        }
    }
}

嘿嘿嘿!!!!

image-20241201235803212