数独问题的c++解决

来源:互联网 发布:淘宝上退款多久能到账 编辑:程序博客网 时间:2024/05/19 20:20

       今天在刷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,达到了预期的效果。