【问题标题】:Minesweeper program exceeding time limit扫雷程序超过时限
【发布时间】:2021-12-14 14:37:37
【问题描述】:

我正在学习普林斯顿大学关于 cousera 的课程,我为以下问题编写了以下代码:

扫雷。 Minesweeper 是一款 1960 年代的电子游戏,在 m×n 网格上播放。目标是使用有关相邻单元格中地雷数量的线索来推断哪些单元格包含隐藏的地雷。编写一个程序 Minesweeper.java,它接受三个整数命令行参数 m、n 和 k,并打印一个 m×n 网格的单元格,其中包含 k 个地雷,使用星号表示地雷和整数表示相邻地雷计数(两个空格每个单元格之间的字符)。为此, 随机均匀地生成一个 m×n 单元格,其中 mn 个单元格中恰好有 k 个包含地雷。 对于每个不包含地雷的单元格,计算相邻地雷的数量(上、下、左、右或对角线)。

每次我提交给评分者;它说超过了 60 秒的限制,它会停止评分。

这是想要的输出

public class Minesweeper {

    public static void main(String[] args) {

        int m = Integer.parseInt(args[0]);
        int n = Integer.parseInt(args[1]);
        int k = Integer.parseInt(args[2]);

        boolean[][] minePositions = new boolean[m + 2][n + 2];
        int[][] grid = new int[m + 2][n + 2];

        int num = 0;
        while (num != k) {
            if ((m * n) == k) {
                for (int f = 1; f <= m; f++) {
                    for (int z = 1; z <= n; z++) {
                        minePositions[f][z] = true;
                    }
                }
                break;
            }
            int r = (int) (Math.random() * (m * n - 1));
            int q = r / n;
            int rem = r % n;
            if (q == 0) {
                q = 1;
            }
            else if (q > (m - 1)) {
                q = m - 1;
            }
            if (rem == 0) {
                rem = 1;
            }
            else if (rem > (n - 1)) {
                rem = n - 1;
            }
            
            if (!minePositions[q][rem]) {
                minePositions[q][rem] = true;
                num++;
            }
        }

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (minePositions[i - 1][j + 1]) grid[i][j]++;
                if (minePositions[i][j + 1]) grid[i][j]++;
                if (minePositions[i + 1][j + 1]) grid[i][j]++;
                if (minePositions[i - 1][j]) grid[i][j]++;
                if (minePositions[i + 1][j]) grid[i][j]++;
                if (minePositions[i - 1][j - 1]) grid[i][j]++;
                if (minePositions[i][j - 1]) grid[i][j]++;
                if (minePositions[i + 1][j - 1]) grid[i][j]++;
            }
            for (int j = 1; j <= n; j++) {
                if (minePositions[i][j]) System.out.print("*  ");
                else System.out.print(grid[i][j] + "  ");
            }
            System.out.print("\n");
        }

    }
}

【问题讨论】:

  • 您可能会在生成非常“密集”雷区的测试中超时,因为您正在使用接受/拒绝测试来放置地雷,如果雷区非常密集,您可能会在那个循环中持续了很长时间。有没有一种方法可以随机分配地雷,而无需测试位置以查看那里是否已经有一个? (提示:是的)

标签: java optimization time minesweeper


【解决方案1】:

我认为使用大小为(m + 2, n + 2) 的数组会混淆您的数学运算。我会继续你的工作,在你的计算中始终使用m + 2n + 2 以避免任何混淆。

int r = (int) (Math.random() * ((m + 2) * (n + 2)));

生成一个可以跨越所有数组单元格的随机数。

int q = r / (n + 2);
int rem = r % (n + 2);

// Increment num only if q and rem are in the ranges and the position does not have a mine on it
if (q > 0 && q <= m && rem > 0 && rem <= n && !minePositions[q][rem]) {
    minePositions[q][rem] = true;
    num++;
}

完整代码

int m = Integer.parseInt(args[0]);
int n = Integer.parseInt(args[1]);
int k = Integer.parseInt(args[2]);

boolean[][] minePositions = new boolean[m + 2][n + 2];
int[][] grid = new int[m + 2][n + 2];

int num = 0;
while (num != k) {
    int r = (int) (Math.random() * ((m + 2) * (n + 2)));
    int q = r / (n + 2);
    int rem = r % (n + 2);

    if (q > 0 && q <= m && rem > 0 && rem <= n && !minePositions[q][rem]) {
        minePositions[q][rem] = true;
        num++;
    }
}

for (int i = 1; i <= m; i++) {
    for (int j = 1; j <= n; j++) {
        if (minePositions[i - 1][j + 1]) grid[i][j]++;
        if (minePositions[i][j + 1]) grid[i][j]++;
        if (minePositions[i + 1][j + 1]) grid[i][j]++;
        if (minePositions[i - 1][j]) grid[i][j]++;
        if (minePositions[i + 1][j]) grid[i][j]++;
        if (minePositions[i - 1][j - 1]) grid[i][j]++;
        if (minePositions[i][j - 1]) grid[i][j]++;
        if (minePositions[i + 1][j - 1]) grid[i][j]++;
    }
    for (int j = 1; j <= n; j++) {
        if (minePositions[i][j]) System.out.print("*  ");
        else System.out.print(grid[i][j] + "  ");
    }
    System.out.print("\n");
}

【讨论】:

  • 好的,我喜欢你生成随机数的计算,你能解释一下吗?另外,您应该考虑这种情况,其中地雷的数量是行和列的乘积,例如 8 4 32,这就是 while 循环中两个嵌套 for 循环的原因
  • @Alinasweklb 你不需要嵌套循环,如果地雷数等于 m * n,无论循环执行多少次都会正确生成,它会生成所有0 到 (n+2)(m+2) - 1 之间的数字
  • 我在没有嵌套循环的情况下尝试过,但程序运行了很长时间。
  • @Alinasweklb 你使用的 m、n 和 k 的值是多少?
  • 我告诉你的同样的 8 4 32
猜你喜欢
  • 2015-05-15
  • 2019-03-12
  • 1970-01-01
  • 1970-01-01
  • 2021-06-10
  • 2014-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多