9

我正在寻求实现一个非常简单的算法,该算法使用蛮力回溯来解决数独网格。我面临的问题是,在我的实现中,我为一个Sudoku名为rowand的类包含了两个实例变量col,它们对应于表示数独网格的二维数组中空单元格的行和列。

当我的solve()方法执行时,它首先检查是否没有任何空单元格,在这种情况下,拼图已经完成。否则,相同的方法将空单元格的行和列分配给实例变量row和包含网格col的对象。Sudoku然后,for循环通过方法调用验证哪个数字可以放在那个空单元格中isSafe(int n)(这个方法检查是否满足拼图的约束,我可以保证它完美地运行)。因此,该isSafe()方法在空单元格中放置一个数字,然后在对象solve()上再次递归调用该方法。Sudoku

如果我们遇到了一个无法满足的约束,那么我们将 a 重新分配0给最后一个row并被col填充。这就是发现问题的地方!由于程序不断更新rowcol变量,因此每次递归调用都会丢失旧实例。我一直在试图弄清楚如何存储这些值,以便程序在回溯时可以撤消操作。我想过将每个col和推row入堆栈,但我真的不知道该去哪里。

有人能告诉我解决这个问题的简单方法是什么吗?我不包括整个班级,如果您认为这会有所帮助,请告诉我,我会发布它。

class Sudoku {
    int SIZE, N, row, col;
    int Grid[][];    

    public boolean solve() {
        if (!this.findNextZero()) return true;

        for (int num = 1; num <= 9; num++) {
            if (isSafe(num)) {
                this.Grid[this.row][this.col] = num;

                if (this.solve()) return true;

                this.Grid[this.row][this.col] = 0;
                // this.Grid[oldRow][oldCol] = 0;
            }
        }
        return false;
    }

    public boolean findNextZero() {
        for (int i = 0; i < this.N; i++) {
            for (int j = 0; j < this.N; j++) {
                if (this.Grid[i][j] == 0) {
                    this.row = i;
                    this.col = j;
                    return true;
                }
            }
        }
        return false;
    }

    public boolean isSafe(int num) {
        return !this.usedInRow(num) 
                && !this.usedInColumn(num) 
                && !this.usedInBox(num);
    }

如果我要实现一个堆栈,以下是否有意义?在findNextZero()操作之后将rowcol整数推入堆栈。继续这样做,然后修改以下代码行

this.Grid[this.row][this.col] = 0;

类似于

this.Grid[s.pop][s.pop] = 0;

这是一个合理的方法吗?

4

2 回答 2

2

实际上,您实际上并不需要堆栈或递归。您只需要一种有序的方式来访问单元格(参见下面的代码)。此解决方案不会像递归版本那样为您提供 stackoverflow。

我会创建一个初始矩阵来标记预先解决的单元格:

public boolean[][] findSolved(int[][] grid){
    boolean[][] isSolved = new boolean[9][9];        

    for(int i=0; i<9; i++)
        for(int j=0; j<9; j++)
            isSolved[i][j] = grid[i][j] != 0;

    return isSolved;
}

然后根据您是否在回溯,向前或向后通过单元格:

public boolean solve(int[][] grid){
    boolean[][] isSolved = findSolved(grid);
    int row, col, k = 0;
    boolean backtracking = false;

    while( k >= 0 && k < 81){
        // Find row and col
        row = k/9;
        col = k%9;

        // Only handle the unsolved cells
        if(!isSolved[row][col]){
            grid[row][col]++;

            // Find next valid value to try, if one exists
            while(!isSafe(grid, row, col) && grid[row][col] < 9)
                grid[row][col]++;

            if(grid[row][col] >= 9){
                // no valid value exists. Reset cell and backtrack
                grid[row][col] = 0;
                backtracking = true;
            } else{
                // a valid value exists, move forward
                backtracking = false;
            }
        }

        // if backtracking move back one, otherwise move forward 1.
        k += backtracking ? -1:1
    }

    // k will either equal 81 if done or -1 if there was no solution.
    return k == 81;
}
于 2013-11-14T07:30:33.187 回答
1

通过将它们存储在数独类的 Stack 实例变量中,我能够存储我在每次递归调用中不断丢失的空单元格的 'row' 和 'col' 值。findNextZero() 方法将 'row' 和 'col' 值推入两个空堆栈。然后,我重组了程序的其余部分以通过 peek() 方法访问此信息,如果我不得不回溯,我只需弹出最后两个值并将与这些值对应的网格上的数字设置为 0。

于 2013-11-14T16:44:53.737 回答