在 C 中使用递归和回溯解决数独字段

问题描述

我的挑战是通过递归和回溯来解决给定的数独领域。我们已经有了一些代码,但我们不知道如何实现回溯。这是我们的代码

int solve( int row,int col ) {
    if (getValueFromField(row,col) != 0) {
        if (col < 8) {
            return solve(row,col + 1);
        }
        if (col == 8) {
            return solve(row + 1,0);
        }
        if (col == 8 && row == 8) {
            return 1;
        }
    } else {
        for (int i = 1; i <= 9; i++) {
            if (checkValueInField(i,row,col)) {
                setValueInField(i,col);
                if (solve(row,col)) {
                    continue;
                }
                return solve(row,col + 1);
            }
        }
        removeValueFromField(row,col);
        return 0;
    }
    return -1;
}

removeValueFromField()setValueInField()方法自行解释。如果没有,我可以向您解释他们是做什么的。

您知道如何在此处实施回溯吗? 问候! 拉斐尔

解决方法

您必须保存之前所做的检查,例如在列表中 每个单元格的可能值。最初,所有列表都包含 数字 1-9。初始化时,给定的列表 数独中的单元格减少到一个成员。

然后,主要算法是对所有单元格的循环,您可能会实现它 作为递归,就像在您的代码中一样。在这个循环中所有数独约束 应用于每个列表,并删除不兼容的情况。 例如:假设 cell(1,1) 是 '1',那么我们可以从第 1 行第 1 列的所有列表中删除 1, 阻止(1,1)等。我们循环直到没有不兼容的情况。 如果列表之一折叠到零长度,则数独无法解决。

如果此时 (A) 两个或多个列表的长度保持大于 1,我们 选择这些列表之一的可能性之一(比如 p),然后开始 再次循环。如果结果无法解决,我们从列表中删除 p, 回到(A)(这是回溯)并尝试第二种可能性,依此类推。 最终,我们将达到一个阶段,除了一个列表之外,所有列表的长度为 1。 数独是可解的,如果最后一个列表的长度 > 0。

这是一个简化版本,它只是找到了一种可能的解决方案。 您必须自己编写原型函数。

int getListLength(row,col); // number of possible values for this cell,0...9

void checkConstrains();         // loop all sets (rows,columns,blocks)
                                // and find lists with one element. Remove
                                // this element from all other lists in the set.
                                // Repeat until no duplicates are found.
                        
int *savelist(row,col);        // make a copy of the list
void setlist(row,col,k);      // set list of cell to { k }
getstate(),setstate(s);        // save and set state of the game,ie. all lists
                                


int solve( int row,int col ) {
        int length = getListLength(row,col);
        if(length == 0)
                return 0; // unsolveable
        
        // this is your recursion step slightly
        // reordered to avoid row-overflow
        
        if (col == 8 && row == 8) {
                return 1;
        }
        
        int next_row,next_col;
        if(col < 8) {
                next_row = row;
                next_col = col + 1;
        } else {
                next_row = row + 1;
                next_col = 0;
        }
        
        if(length == 1) {
                return solve(next_row,next_col);
        }
        
        // if we reach this step: length > 1
        checkConstrains();
        
        // length might have changed
        length = getListLength(row,col);
        
        if(length == 0)
                return 0;
        if(length == 1)
                return solve(next_row,next_col);
        
        // still: length > 1
        // backtracking stage
        
        int* list = savelist(row,col);
        sudoku s = getstate();
        
        for(int i = 0; i < length; i++) {
                setstate(s);
                setlist(row,list[i]);
                if(solve(row,col))
                        return 1;
        }
        // all elements from list failed-->unsolveable
        // free list and s
        return 0;
}