数独问题的c++解决

       今天在刷leetcode的时候遇见一道难度为hard的题目,大意是解出给定的数独。感觉比较有应用的价值,便尝试着去做了一下。

       首先明确数独问题必有唯一可行解。求数独有效解的基本思想是利用回溯法:从挖空的地方开始,从1到9逐个地去尝试可能的解。如果当前行、列以及所在大方格没有出现重复,则解被暂时接受,并开始尝试以相同的方式求解下一个空格。如果1到9均不成为有效解,则后退至上一个空格,试探其下一个可能值。以下面一个简单的数独为例:

       搜索空格。在(1,3)处出现空格。从“1”开始试探,发现“2”符合要求,将其暂时填入,并搜索下一个空格(1,4)。发现符合条件的数为“6”,将其填入空格。如下图所示:

       此时开始搜索第三个空格(1,9),发现1到9中无合适的数字可填入其中,故回退至上一“空格”处(1,4),此时同样发现可行解也已搜索完毕,故再回退至上上空格(1,3)处,搜索得下一可能值4,将其填入,如下所示:

       此时其他空格便迎刃而解了。具体实现的代码如下:

class Solution {
public:
    bool isvalid(vector<vector<char>>& board, int row, int column, char test){          //检查新添加的元素是否符合数独要求
        int st1=row/3*3,st2=column/3*3;          //同一方块下的首元素位置坐标        
        for(int i=0;i<9;i++)
        {
            if(board[i][column]==test) return false;           //检查同一列
            if(board[row][i]==test) return false;           //检查同一行
            if(board[st1+i/3][st2+i%3]==test) return false;          //检查同一方块
        }
        return true;
    }
    bool solve(vector<vector<char>>& board) {
        int rows=board.size(),columns=board[0].size();
        for(int i=0;i<rows;i++)
            for(int j=0;j<columns;j++)
            {
                if(board[i][j]=='.')
                {
                    for(char c='1';c<='9';c++)          //逐个尝试
                    {
                        if(isvalid(board,i,j,c))          //初步尝试
                        {
                            board[i][j]=c;
                            if(solve(board))
                                return true;
                            else
                                board[i][j]='.';          //回溯
                        }        
                    }
                    return false;            //如果九个数都试过还是不符合要求,则需要回溯
                }
            }
        return true;            //此时已无空格
    }
    void solveSudoku(vector<vector<char>>& board) {          //使用回溯法解决
        solve(board);
    }
};
       经过测试,发现五个case的解答时间平均为13ms。该算法的时间复杂度为O(9^N),其中N是未知空格数。不难发现,在程序中很多地方进行了重复的遍历,仍有进行剪枝以进一步降低运行时间的可能。考虑用三个数组分别记录每一行、每一列以及每一个大方格内出现数字的情况,并将数独内所有挖空处的行列信息统一用一个数组保存,减少重复遍历。具体实现代码如下所示:

class Solution {
public:
    bool solver(vector<vector<char>>& board,int index,vector<pair<int,int>>& empt, vector<vector<bool>>& row, vector<vector<bool>>& column, vector<vector<bool>>& region)
    {
        if(index==empt.size()) return true;          //此时已找到所有空格上所应填的数
        int r=empt[index].first,c=empt[index].second;          
        for(int a=0;a<9;a++)
        {
            if(!row[r][a]&&!column[c][a]&&!region[r/3*3+c/3][a])          //如果没有重复
            {
                row[r][a]=true;
                column[c][a]=true;
                region[r/3*3+c/3][a]=true;
                board[r][c]='1'+a;
                if(solver(board,index+1,empt,row,column,region)) return true;          //表明一路返回的都是true
                else
                {
                    row[r][a]=false;           //否则,需要将状态还原以回溯
                    column[c][a]=false;
                    region[r/3*3+c/3][a]=false;
                    board[r][c]='.';
                }
            }         
        }
        return false;          //遍历了所有可能性仍然没有符合要求的结果
    }
    void solveSudoku(vector<vector<char>>& board) {          //依然是回溯法,不过用空间换时间,进行了一定程度的剪枝
        vector<vector<bool>> row(9,vector<bool>(9,false));          //记录每一行的数字是否已经出现过的情况
        vector<vector<bool>> column(9,vector<bool>(9,false));
        vector<vector<bool>> region(9,vector<bool>(9,false));          
        vector<pair<int,int>> empt;          //记录需要填满的空格的坐标
        int l1=board.size(),l2=board[0].size();
        for(int i=0;i<l1;i++)
            for(int j=0;j<l2;j++)          //所有数据进行初始化
            {
                if(board[i][j]=='.')
                {
                    empt.push_back({i,j});
                }
                else
                {
                    int num=board[i][j]-'1';
                    row[i][num]=true;
                    column[j][num]=true;
                    region[i/3*3+j/3][num]=true;
                }
            }
        solver(board,0,empt,row,column,region);
    }
};
      经过测试,相同case下时间降到了6ms,达到了预期的效果。

猜你喜欢

转载自blog.csdn.net/xbb123456rt/article/details/78452520