一、题目

leetcode_37解数独_状态压缩

 

 leetcode_37解数独_状态压缩_02

 

 2.思路分析

    我本来觉得如果单纯使用遍历的话太过复杂,所以想使用按照人去数独的方法去解决,但是这样写下来,对于人来说很简单的逻辑,代码实现反而很复杂,但是对于搜索这种看起来对人很复杂的程序,对算法和计算机而言反而很简单。

    我最开始想采取的做法是,先使用数据计算每行空格数,每列空格数,每个小块空格数,以及每三格的空格数,然后查找每个元素1-9,获得两个相同元素的位置,比如找到两个1的横纵坐标,然后验证其行列交点所在方块是否存在单一空缺值(即排除掉两个1所在行列以后),然后这样不断计算,但是这样发现逻辑就很复杂。

我的代码如下,但是没有跑通。

leetcode_37解数独_leetcode_03leetcode_37解数独_数组_04
  1 class Solution {
  2 public:
  3     void solveSudoku(vector<vector<char>>& board) {
  4         //每行为0的数目
  5         int row[9] = {0};
  6         //每列为0的数目
  7         int col[9] = {0};
  8         //行block
  9         int row_block[27] = {0};
 10         //列block
 11         int col_block[27] = {0};
 12         //每块为0的数目
 13         int block[9] = {0};
 14         vector<int> num_count = {0,0,0,0,0,0,0,0,0};
 15         vector<vector<int>> point_x(9);
 16         vector<vector<int>> point_y(9);
 17 
 18         //初始化
 19         //查找每个数字的个数
 20         //以及每行、每列、每个块的空缺数目
 21         for(int i=0; i<9; i++)
 22         {
 23             for(int j=0; j<9; j++)
 24             {
 25                 int num = board[i][j] - '0';
 26                 if (num<=9 && num>=1)
 27                 {
 28                     num_count[num-1] = num_count[num-1] +1;
 29                     //统计元素出现位置
 30                     point_x[num-1].push_back(i);
 31                     point_y[num-1].push_back(j);
 32                 }  
 33                 else
 34                 {
 35                     row[i]++;
 36                     col[j]++;
 37                     row_block[i*3+(j/3)]++;
 38                     col_block[(i/3)*9+j]++;
 39                     block[(i/3)*3 + j/3]++;
 40                 }
 41             }
 42         }
 43         while(accumulate(num_count.begin(),num_count.end(),0)!=81)
 44         {
 45             //检查是否存在只有一个空缺值
 46             for( int i=0; i<9; i++)
 47             {
 48                 //如果行只有一个空缺值
 49                 if(row[i]==1)
 50                 {
 51                     int loc=0;
 52                     int sum=0;
 53                     for(int j=0; j<9; j++)
 54                     {
 55                         if (board[i][j]=='.')
 56                             loc=j;
 57                         else
 58                             sum = sum + board[i][j]-'0'; 
 59                     }
 60                     num_count[45-sum-1]++;
 61                     point_x[45-sum-1].push_back(i);
 62                     point_y[45-sum-1].push_back(loc);
 63                     board[i][loc] = 45-sum + '0';
 64                     row[i]--;
 65                     col[loc] --;
 66                     row_block[i*3+(loc/3)]--;
 67                     col_block[(i/3)*9+loc]--;
 68                     block[(i/3)*3 + loc/3]--;
 69                 }
 70                 //如果列只有一个空缺值
 71                 else if (col[i]==1)
 72                 {
 73                     int loc=0;
 74                     int sum=0;
 75                     for(int j=0; j<9; j++)
 76                     {
 77                         if (board[j][i]=='.')
 78                             loc=j;
 79                         else
 80                             sum = sum + board[j][i]-'0'; 
 81                     }
 82                     num_count[45-sum-1]++;
 83                     point_x[45-sum-1].push_back(loc);
 84                     point_y[45-sum-1].push_back(i);
 85                     board[loc][i] = 45-sum + '0';
 86                     row[loc]--;
 87                     col[i] --;
 88                     row_block[loc*3+(i/3)]--;
 89                     col_block[(loc/3)*9+i]--;
 90                     block[(loc/3)*3 + i/3]--;
 91                 }
 92                 //如果块只有一个空缺值
 93                 else if (block[i]==1)
 94                 {
 95                     int loc=0;
 96                     int sum=0;
 97                     for(int j=0; j<9; j++)
 98                     {
 99                         if (board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]=='.')
100                             loc=j;
101                         else
102                             sum = sum + board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]-'0'; 
103                     }
104                     num_count[45-sum-1]++;
105                     point_x[45-sum-1].push_back((i/3)*3 + loc/3);
106                     point_y[45-sum-1].push_back((i%3)*3 + (loc%3));
107                     board[(i/3)*3 + loc/3][(i%3)*3 + (loc%3)]= 45-sum + '0';
108                     row[(i/3)*3 + loc/3]--;
109                     col[(i%3)*3 + (loc%3)] --;
110                     row_block[((i/3)*3 + loc/3)*3+(((i%3)*3 + (loc%3))/3)]--;
111                     col_block[(((i/3)*3 + loc/3)/3)*9+((i%3)*3 + (loc%3))]--;
112                     block[i]--;
113                 }
114             }
115             //下标
116             for(int pre_num=0; pre_num<9; pre_num++)
117             {
118                 char pre_char_number = pre_num+'1';
119                 //长度
120                 int length = point_x[pre_char_number].size();
121                 //根据互斥性查找位置
122                 if (length>=2)
123                 {
124                     for(int start=0; start<length-1;start++)
125                     {
126                         for(int end=start+1; end<length; end++)
127                         {
128                             //获得point_x和point_y的对应位置元素
129                             int start_x = point_x[pre_num][start];
130                             int start_y = point_y[pre_num][start];
131                             int end_x = point_x[pre_num][end];
132                             int end_y = point_y[pre_num][end];
133                             //判断交点位置是否为空
134                             //如果为空
135                             if (board[start_x][end_y] !='.')
136                             {
137                                 if(block[(start_x/3)*3 + end_y/3]>=2)
138                                 {
139                                     //如果只剩下一个空缺值
140                                     if(block[(start_x/3)*3 + end_y/3] - row_block[start_x*3+(end_y/3)] - col_block[(start_x/3)*9+end_y]==1)
141                                     {
142                                         //求解该block空缺值
143                                         //block序号
144                                         int i = (start_x/3)*3 + end_y/3;
145                                         int loc=0;
146                                         int sum=0;
147                                         for(int j=0; j<9; j++)
148                                         {
149                                             if (board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]=='.')
150                                                 loc=j;
151                                             else
152                                                 sum = sum + board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]-'0'; 
153                                         }
154                                         num_count[45-sum-1]++;
155                                         point_x[45-sum-1].push_back((i/3)*3 + loc/3);
156                                         point_y[45-sum-1].push_back((i%3)*3 + (loc%3));
157                                         board[(i/3)*3 + loc/3][(i%3)*3 + (loc%3)]= 45-sum + '0';
158                                         row[(i/3)*3 + loc/3]--;
159                                         col[(i%3)*3 + (loc%3)] --;
160                                         row_block[((i/3)*3 + loc/3)*3+(((i%3)*3 + (loc%3))/3)]--;
161                                         col_block[(((i/3)*3 + loc/3)/3)*9+((i%3)*3 + (loc%3))]--;
162                                         block[i]--;
163                                     }
164                                 }
165                             }
166                             else 
167                             {
168                                 if(block[(start_x/3)*3 + end_y/3]>=2)
169                                 {
170                                     //如果只剩下一个空缺值
171                                     if(block[(start_x/3)*3 + end_y/3] - row_block[start_x*3+(end_y/3)] - col_block[(start_x/3)*9+end_y]==0)
172                                     {
173                                         //求解该block空缺值
174                                         //block序号
175                                         int i = (start_x/3)*3 + end_y/3;
176                                         int loc=0;
177                                         int sum=0;
178                                         for(int j=0; j<9; j++)
179                                         {
180                                             if (board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]=='.')
181                                                 loc=j;
182                                             else
183                                                 sum = sum + board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]-'0'; 
184                                         }
185                                         num_count[45-sum-1]++;
186                                         point_x[45-sum-1].push_back((i/3)*3 + loc/3);
187                                         point_y[45-sum-1].push_back((i%3)*3 + (loc%3));
188                                         board[(i/3)*3 + loc/3][(i%3)*3 + (loc%3)]= 45-sum + '0';
189                                         row[(i/3)*3 + loc/3]--;
190                                         col[(i%3)*3 + (loc%3)] --;
191                                         row_block[((i/3)*3 + loc/3)*3+(((i%3)*3 + (loc%3))/3)]--;
192                                         col_block[(((i/3)*3 + loc/3)/3)*9+((i%3)*3 + (loc%3))]--;
193                                         block[i]--;
194                                     }
195                                 }
196                             }
197                             if(board[end_x][start_y]!='.')
198                             {
199                                 if(block[(end_x/3)*3 + start_y/3]>=2)
200                                 {
201                                     //如果只剩下一个空缺值
202                                     if(block[(end_x/3)*3 + start_y/3] - row_block[end_x*3+(start_y/3)] - col_block[(end_x/3)*9+start_y]==1)
203                                     {
204                                         //求解该block空缺值
205                                         //block序号
206                                         int i = (end_x/3)*3 + start_y/3;
207                                         int loc=0;
208                                         int sum=0;
209                                         for(int j=0; j<9; j++)
210                                         {
211                                             if (board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]=='.')
212                                                 loc=j;
213                                             else
214                                                 sum = sum + board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]-'0'; 
215                                         }
216                                         num_count[45-sum-1]++;
217                                         point_x[45-sum-1].push_back((i/3)*3 + loc/3);
218                                         point_y[45-sum-1].push_back((i%3)*3 + (loc%3));
219                                         board[(i/3)*3 + loc/3][(i%3)*3 + (loc%3)]= 45-sum + '0';
220                                         row[(i/3)*3 + loc/3]--;
221                                         col[(i%3)*3 + (loc%3)] --;
222                                         row_block[((i/3)*3 + loc/3)*3+(((i%3)*3 + (loc%3))/3)]--;
223                                         col_block[(((i/3)*3 + loc/3)/3)*9+((i%3)*3 + (loc%3))]--;
224                                         block[i]--;
225                                     }
226                                 }
227                             }
228                             else
229                             {
230                                 if(block[(end_x/3)*3 + start_y/3]>=2)
231                                 {
232                                     //如果只剩下一个空缺值
233                                     if(block[(end_x/3)*3 + start_y/3] - row_block[end_x*3+(start_y/3)] - col_block[(end_x/3)*9+start_y]==0)
234                                     {
235                                         //求解该block空缺值
236                                         //block序号
237                                         int i = (end_x/3)*3 + start_y/3;
238                                         int loc=0;
239                                         int sum=0;
240                                         for(int j=0; j<9; j++)
241                                         {
242                                             if (board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]=='.')
243                                                 loc=j;
244                                             else
245                                                 sum = sum + board[(i/3)*3 + j/3][(i%3)*3 + (j%3)]-'0'; 
246                                         }
247                                         num_count[45-sum-1]++;
248                                         point_x[45-sum-1].push_back((i/3)*3 + loc/3);
249                                         point_y[45-sum-1].push_back((i%3)*3 + (loc%3));
250                                         board[(i/3)*3 + loc/3][(i%3)*3 + (loc%3)]= 45-sum + '0';
251                                         row[(i/3)*3 + loc/3]--;
252                                         col[(i%3)*3 + (loc%3)] --;
253                                         row_block[((i/3)*3 + loc/3)*3+(((i%3)*3 + (loc%3))/3)]--;
254                                         col_block[(((i/3)*3 + loc/3)/3)*9+((i%3)*3 + (loc%3))]--;
255                                         block[i]--;
256                                     }
257                                 }
258                             }
259                         }
260                     }
261                 }
262             }
263         }
264     }
265 };
View Code
3.实例代码

    3.1参考链接

    代码主要参考链接如下:https://leetcode-cn.com/problems/sudoku-solver/solution/37-by-ikaruga/

 3.2.主要方法

leetcode_37解数独_状态压缩_05

 

  3.3代码

leetcode_37解数独_leetcode_03leetcode_37解数独_数组_04
class Solution {
public:
    bitset<9> getPossibleStatus(int x, int y)
    {
        return ~(rows[x] | cols[y] | cells[x / 3][y / 3]);
    }

    vector<int> getNext(vector<vector<char>>& board)
    {
        vector<int> ret;
        int minCnt = 10;
        for (int i = 0; i < board.size(); i++)
        {
            for (int j = 0; j < board[i].size(); j++)
            {
                if (board[i][j] != '.') continue;
                auto cur = getPossibleStatus(i, j);
                if (cur.count() >= minCnt) continue;
                ret = { i, j };
                minCnt = cur.count();
            }
        }
        return ret;
    }

    void fillNum(int x, int y, int n, bool fillFlag)
    {
        rows[x][n] = (fillFlag) ? 1 : 0;
        cols[y][n] = (fillFlag) ? 1 : 0;
        cells[x/3][y/3][n] = (fillFlag) ? 1: 0;
    }
    
    bool dfs(vector<vector<char>>& board, int cnt)
    {
        if (cnt == 0) return true;

        auto next = getNext(board);
        auto bits = getPossibleStatus(next[0], next[1]);
        for (int n = 0; n < bits.size(); n++)
        {
            if (!bits.test(n)) continue;
            fillNum(next[0], next[1], n, true);
            board[next[0]][next[1]] = n + '1';
            if (dfs(board, cnt - 1)) return true;
            board[next[0]][next[1]] = '.';
            fillNum(next[0], next[1], n, false);
        }
        return false;
    }

    void solveSudoku(vector<vector<char>>& board) 
    {
        rows = vector<bitset<9>>(9, bitset<9>());
        cols = vector<bitset<9>>(9, bitset<9>());
        cells = vector<vector<bitset<9>>>(3, vector<bitset<9>>(3, bitset<9>()));

        int cnt = 0;
        for (int i = 0; i < board.size(); i++)
        {
            for (int j = 0; j < board[i].size(); j++)
            {
                cnt += (board[i][j] == '.');
                if (board[i][j] == '.') continue;
                int n = board[i][j] - '1';
                rows[i] |= (1 << n);
                cols[j] |= (1 << n);
                cells[i / 3][j / 3] |= (1 << n);
            }
        }
        dfs(board, cnt);
    }

private:
    vector<bitset<9>> rows;
    vector<bitset<9>> cols;
    vector<vector<bitset<9>>> cells;
};
View Code
4.方法总结

    4.1算法流程

1-开始
2-设置变量存储每行、每列、每块1-9是否出现
3-初始化 - 统计每行、每列、每块中数字1-9是否出现,用状态表示
4-统计空缺值的数目
4-深度优先搜索
    计算可能数字最少的空缺位置
    得到可能数值
    遍历每一个数值
    填写数值,状态更新
    进行dfs,深度优先搜索,探索所有的可能性
    if 最后空缺值数据为0
       回退
    else
       最后的空缺值不为0,则说明这种探索的可能性不正确,将该空格重新设置为‘.’,重新状态更新
    

    4-2 有益的借鉴

     1-从直觉上来说,我总是觉得使用与人做数独类似的方法会更快,但是程序表明了,使用搜索的方法反而是计算机最擅长的事情

     2-使用bitset<9>这种单个bit来表示状态是一个很好的方式,特别是状态特别多的时候,这种可以节省空间。

         bitset<n>存在几种用法:移位、计数、测试是否为1、逻辑运算

     3-深度优先搜索很有用

     4-这种状态表示方法其实比我的方法更好更仔细一点。

  

     

 

纵一苇之所如,临万顷之茫然。