码迷,mamicode.com
首页 > 其他好文 > 详细

LeetCode 36/37. Valid Sudoku/ Sudoku Solver

时间:2016-05-07 10:06:58      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:

1. 题目分析

36

Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules.
The Sudoku board could be partially filled, where empty cells are filled with the character ‘.’.
技术分享

A partially filled sudoku which is valid.

Note:
A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated.

37

Write a program to solve a Sudoku puzzle by filling the empty cells.
Empty cells are indicated by the character ‘.’.
技术分享
You may assume that there will be only one unique solution.
技术分享
A sudoku puzzle…
…and its solution numbers marked in red.

2. 解题思路

valid sudoku 的主要思路很简单, 只要确保行, 列, 每个方块中都没有重复元素即可。
而sudoku solver 其实也很简单, 只需要借助回溯的思想求解即可

3. code

3.1 36

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        return isOK(board);
    }

private:
    bool isOK(vector<vector<char>>& board){
        if (board.size() == 0)
            return false;

        for (int i = 0; i != board.size(); i++){
            set<char> myset;
            for (int j = 0; j != board[0].size(); j++){
                if (board[i][j] != ‘.‘){
                    if (myset.count(board[i][j]) > 0)
                        return false;
                    myset.insert(board[i][j]);
                }                   
            }
        }

        for (int j = 0; j != board[0].size(); j++){
            set<char> myset;
            for (int i = 0; i != board.size(); i++){
                if (board[i][j] != ‘.‘){
                    if (myset.count(board[i][j]) > 0)
                        return false;
                    myset.insert(board[i][j]);
                }
            }
        }

        for (int i = 0; i != board.size() / 3; i++){
            for (int j = 0; j != board[0].size() / 3; j++){
                set<char> myset;
                for (int ii = 3 * i; ii != 3 * i + 3; ii++){
                    for (int jj = 3 * j; jj != 3 * j + 3; jj++){
                        if (board[ii][jj] != ‘.‘){
                            if (myset.count(board[ii][jj]) > 0)
                                return false;
                            myset.insert(board[ii][jj]);
                        }
                    }
                }
            }
        }

        return true;
    }
};

3.2 37

class Solution {
public:
    void solveSudoku(vector<vector<char>>& board) {
        vector<int> pos;
        for (int i = 0; i != board.size(); i++){
            for (int j = 0; j != board[0].size(); j++){
                if (board[i][j] == ‘.‘){
                    pos.push_back(i * 9 + j);
                }
            }
        }
        DFS(board, pos, 0);
    }

private:
    bool isOK(vector<vector<char>>& board, int row, int col){
        for (int i = 0; i != 9; i++){
            if (i != row && board[i][col] == board[row][col])
                return false;

            if (i != col && board[row][col] == board[row][i])
                return false;
        }

        int start_row = row / 3;
        int start_col = col / 3;
        for (int i = 3 * start_row; i != 3 * start_row + 3; i++){
            for (int j = 3 * start_col; j != 3 * start_col + 3; j++){
                if (i != row && j != col && board[i][j] == board[row][col])
                    return false;
            }
        }
        return true;
    }

    bool DFS(vector<vector<char>>& board, vector<int> & pos, int depth){
        if (depth >= pos.size())
            return true;

        int row = pos[depth] / 9;
        int col = pos[depth] % 9;
        int tmp = board[row][col];
        for (int i = 1; i != 10; i++){
            board[row][col] = i + ‘0‘;
            if (isOK(board, row, col)){
                bool ret = DFS(board, pos, depth + 1);
                if (ret)
                    return ret;
            }               
        }
        board[row][col] = tmp;
        return false;
    }
};

4. 大神解法

4.1 36

so clear !!!

/*
Three flags are used to check whether a number appear.
used1: check each row
used2: check each column
used3: check each sub-boxes
*/
class Solution
{
public:
    bool isValidSudoku(vector<vector<char> > &board)
    {
        int used1[9][9] = {0}, used2[9][9] = {0}, used3[9][9] = {0};

        for(int i = 0; i < board.size(); ++ i)
            for(int j = 0; j < board[i].size(); ++ j)
                if(board[i][j] != ‘.‘)
                {
                    int num = board[i][j] - ‘0‘ - 1, k = i / 3 * 3 + j / 3;
                    if(used1[i][num] || used2[j][num] || used3[k][num])
                        return false;
                    used1[i][num] = used2[j][num] = used3[k][num] = 1;
                }

        return true;
    }
};

4.2 37 (1)

其实, 个人感觉这个算法, 并不比我们的算法来的好

public class Solution {
    public void solveSudoku(char[][] board) {
        if(board == null || board.length == 0)
            return;
        solve(board);
    }

    public boolean solve(char[][] board){
        for(int i = 0; i < board.length; i++){
            for(int j = 0; j < board[0].length; j++){
                if(board[i][j] == ‘.‘){
                    for(char c = ‘1‘; c <= ‘9‘; c++){//trial. Try 1 through 9 for each cell
                        if(isValid(board, i, j, c)){
                            board[i][j] = c; //Put c for this cell

                            if(solve(board))
                                return true; //If it‘s the solution return true
                            else
                                board[i][j] = ‘.‘; //Otherwise go back
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }

    public boolean isValid(char[][] board, int i, int j, char c){
        //Check colum
        for(int row = 0; row < 9; row++)
            if(board[row][j] == c)
                return false;

        //Check row
        for(int col = 0; col < 9; col++)
            if(board[i][col] == c)
                return false;

        //Check 3 x 3 block
        for(int row = (i / 3) * 3; row < (i / 3) * 3 + 3; row++)
            for(int col = (j / 3) * 3; col < (j / 3) * 3 + 3; col++)
                if(board[row][col] == c)
                    return false;
        return true;
    }
}

4.3 37(2)

超快速的2ms的解法

/*
Update: there‘s a follow-up 0ms solution which is even more optimized

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.

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.

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.

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".
*/
class Solution {
    struct cell // encapsulates a single cell on a Sudoku board
    {
        uint8_t value; // cell value 1..9 or 0 if unset
        // number of possible (unconstrained) values for the cell
        uint8_t numPossibilities;
        // if bitset[v] is 1 then value can‘t be v
        bitset<10> constraints;
        cell() : value(0), numPossibilities(9),constraints() {};
    };
    array<array<cell,9>,9> cells;

    // sets the value of the cell to [v]
    // the function also propagates constraints to other cells and deduce new values where possible
    bool set(int i, int j, int v)
    { 
        // updating state of the cell
        cell& c = cells[i][j];
        if (c.value == v)
            return true;
        if (c.constraints[v])
            return false;
        c.constraints = bitset<10>(0x3FE); // all 1s
        c.constraints.reset(v);
        c.numPossibilities = 1;
        c.value = v;

        // propagating constraints
        for (int k = 0; k<9; k++) {
            // to the row: 
            if (i != k && !updateConstraints(k, j, v))
                return false;
            // to the column:
            if (j != k && !updateConstraints(i, k, v))
                return false;
            // to the 3x3 square:
            int ix = (i / 3) * 3 + k / 3;
            int jx = (j / 3) * 3 + k % 3;
            if (ix != i && jx != j && !updateConstraints(ix, jx, v))
                return false;
        }
        return true;
    }
    // update constraints of the cell i,j by excluding possibility of ‘excludedValue‘
    // once there‘s one possibility left the function recurses back into set()
    bool updateConstraints(int i, int j, int excludedValue)
    {
        cell& c = cells[i][j];
        if (c.constraints[excludedValue]) {
            return true;
        }
        if (c.value == excludedValue) {
            return false;
        }
        c.constraints.set(excludedValue);
        if (--c.numPossibilities > 1)
            return true;
        for (int v = 1; v <= 9; v++) {
            if (!c.constraints[v]) {
                return set(i, j, v);
            }
        }
        assert(false);
    }

    // backtracking state - list of empty cells
    vector<pair<int, int>> bt;

    // find values for empty cells
    bool findValuesForEmptyCells()
    {
        // collecting all empty cells
        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));
            }
        }
        // making backtracking efficient by pre-sorting empty cells by numPossibilities
        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 backtrack(0);
    }

    // Finds value for all empty cells with index >=k
    bool backtrack(int k)
    {
        if (k >= bt.size())
            return true;
        int i = bt[k].first;
        int j = bt[k].second;
        // fast path - only 1 possibility
        if (cells[i][j].value)
            return backtrack(k + 1);
        auto constraints = cells[i][j].constraints;
        // slow path >1 possibility.
        // making snapshot of the state
        array<array<cell,9>,9> snapshot(cells);
        for (int v = 1; v <= 9; v++) {
            if (!constraints[v]) {
                if (set(i, j, v)) {
                    if (backtrack(k + 1))
                        return true;
                }
                // restoring from snapshot,
                // note: computationally this is cheaper
                // than alternative implementation with undoing the changes
                cells = snapshot;
            }
        }
        return false;
    }
public:
    void solveSudoku(vector<vector<char>> &board) {
        cells = array<array<cell,9>,9>(); // clear array
        // Decoding input board into the internal cell matrix.
        // As we do it - constraints are propagated and even additional values are set as we go
        // (in the case if it is possible to unambiguously deduce them).
        for (int i = 0; i < 9; i++)
        {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != ‘.‘ && !set(i, j, board[i][j] - ‘0‘))
                    return; // sudoku is either incorrect or unsolvable
            }
        }
        // if we‘re lucky we‘ve already got a solution,
        // however, if we have empty cells we need to use backtracking to fill them
        if (!findValuesForEmptyCells())
            return; // sudoku is unsolvable

        // copying 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‘;
            }
        }
    }
};

LeetCode 36/37. Valid Sudoku/ Sudoku Solver

标签:

原文地址:http://blog.csdn.net/zhyh1435589631/article/details/51333438

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!