leetcode_43 Sudoku Solver

161 篇文章 0 订阅

数独解决方案

Write a program to solve a Sudoku puzzle by filling the empty cells.

A sudoku solution must satisfy all of the following rules:

  1. Each of the digits 1-9 must occur exactly once in each row.
  2. Each of the digits 1-9 must occur exactly once in each column.
  3. Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.

Empty cells are indicated by the character '.'.

                 

 

Note:

  • The given board contain only digits 1-9 and the character '.'.
  • You may assume that the given Sudoku puzzle will have a single unique solution.
  • The given board size is always 9x9.

解决方法:更新反馈与回溯法

This is one of the fastest Sudoku solvers I've ever written. It is compact enough - just 150 lines of C++ code with comments. I thought it'd be interesting to share it, since it combines several techniques like reactive network update propagation and backtracking with very aggressive pruning.  更新传播反馈网络,回溯,剪枝

The algorithm is online - it starts with an empty board and as you add numbers to it, it starts solving the Sudoku.

开始board是空的,一旦向里面填充数字,就开始解决数独

Unlike in other solutions where you have bitmasks of allowed/disallowed values per row/column/square, this solution track bitmask for every(!) cell, forming a set of constraints for the allowed values for each particular cell. Once a value is written into a cell, new constraints are immediately propagated to row, column and 3x3 square of the cell. If during this process a value of other cell can be unambiguously deduced - then the value is set, new constraints are propagated, so on.... You can think about this as an implicit reactive network of cells.  使用为掩码来约束,每个位置可以填充的数字,一旦有新数字填入,约束会更新,(行,列,以及3*3),在更新过程中,如果可以选择的数字降为1,则这个数字会被设置,同时更新约束(行,列,3*3)

If we're lucky (and we'll be lucky for 19 of 20 of Sudokus published in magazines) then Sudoku is solved at the end (or even before!) processing of the input. 如果很幸运,数字都被填充了,接结束了

Otherwise, there will be empty cells which have to be resolved. Algorithm uses backtracking for this purpose. To optimize it, algorithm starts with the cell with the smallest ambiguity. This could be improved even further by using priority queue (but it's not implemented here). Backtracking is more or less standard, however, at each step we guess the number, the reactive update propagation comes back into play and it either quickly proves that the guess is unfeasible or significantly prunes the remaining search space.    否则这里仍有空的地方。算法中使用回溯法来填充这些剩余空的地方,从numPossibilities小的开始回溯。

It's interesting to note, that in this case taking and restoring snapshots of the compact representation of the state is faster than doing backtracking rollback by "undoing the moves".  在回溯前,保存cells的状态,为了上一次回溯对cells的影响。

注意: bitset   array   pair   make_pair    

class Solution {
public:
    struct cell{
        uint8_t value;
        uint8_t numPossibilities;
        bitset<10> constrains;
        cell():value(0),numPossibilities(9),constrains(){};
    };
    array<array<cell,9>,9> cells;
    
    bool setValue(int i,int j,int val)
    {
        cell  &c = cells[i][j];
        if(c.value == val)
        {
            return true;
        }
        if(c.constrains[val])
        {
            return false;
        }
        
        c.constrains = bitset<10>(0x3FE);
        c.constrains.reset(val);
        c.numPossibilities = 1;
        c.value = val;
        for(int k = 0; k < 9; k++)
        {
            /* update row */
            if(k != j && !updateConstrains(i,k,val))
            {
                return false;
            }
            /* update col */
            if(k != i && !updateConstrains(k,j,val))
            {
                return false;
            }
                    
            /* update 3*3 */
            int lx = i/3 * 3 + k/3;
            int ly = j/3 * 3 + k%3;
                    
            if(lx != i && ly != j && !updateConstrains(lx,ly,val))
            {
                return false;
            }
        }
            
        return true;
    }
    
    bool updateConstrains(int x, int y, int val)
    {
        cell &c = cells[x][y];
        
        if(c.constrains[val])
        {
            return true;
        }
        if(c.value == val)
        {
            return false;
        }
        c.constrains.set(val);
        
        if(--c.numPossibilities > 1)
        {
            return true;
        }
        
        /* fill cells[i][j]  using the left 1 */
        for(int v = 1; v <= 9; v++)
        {
            if(!c.constrains[v])
                return setValue(x,y,v);
        }
        return false;
    }
    
    vector<pair<int,int> > bt;
    
    bool findValuesforEmptyCells()
    {
        bt.clear();
        for(int i = 0; i < 9; i++)
        {
            for(int j = 0; j < 9; j++)
            {
                if(!cells[i][j].value)
                {
                    bt.push_back(make_pair(i,j));
                }
            }
        }
        
        sort(bt.begin(),bt.end(),[this](const pair<int,int> &a, const pair<int,int> &b){
            return cells[a.first][a.second].numPossibilities < cells[b.first][b.second].numPossibilities;
        });
        return backtrace(0);
    }
    
    bool backtrace(int k)
    {
        if(k >= bt.size())
            return true;
        int x = bt[k].first;
        int y = bt[k].second;
        
        if(cells[x][y].value)
        {
            return backtrace(k + 1);
        }
        
        auto cons = cells[x][y].constrains;
        
        array<array<cell,9>,9> snapshot(cells);
        
        for(int i = 1; i <= 9; i++)
        {
            if(!cons[i]){
                if(setValue(x,y,i)){
                    if(backtrace(k + 1))
                        return true;
                }
                cells = snapshot;
            }
        }
        return false;
    }

    void solveSudoku(vector<vector<char>>& board) {
        /* clear cells */
        cells = array<array<cell,9>,9>();
        
        /* init */
        for(int i = 0; i < 9; i++)
        {
            for(int j = 0; j < 9; j++)
            {
                if(board[i][j] != '.' && !setValue(i,j,board[i][j]-'0'))
                {
                    return;      /* incorrect or unsolvable */
                }
            }
        }
        
        /* find empty value and backtrace */
        if(!findValuesforEmptyCells())
        {
            return;    /* unsolvable */
        }
        
        /* copy the solution back to the board */
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                if(cells[i][j].value)
                {
                    board[i][j] = cells[i][j].value + '0';
                }   
            }
        }
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值