Wednesday, June 17, 2015

DFS game solver: leetcode35: valid sudoku / Leetcode 36: sudoku solver / Leetcode 51: eight Queen

Leetcode 36: valid sudoku

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 '.'.

本题求一个sudoku是否是valid,一个valid的sudoku必须满足, 每一行每一列的数字都是1-9,并且没有重复的数字,每一个3*3的小九宫格也不能有重复的数字,这个题目中,只考虑数字重复的情况,而不去考虑数字是不是在1-9之间。
要判断数字在1-9之间是否重复,由于是连续的数字,可以直接用一个一维数组size为10,(1-9+'.'),思想和bitvector相同, index为当前数字,value为标记,如果当前数字在数组里面标记为1,那么说明该数字之前出现过,此时出现重复情况。 对于每一行每一列可以用一个二重循环来进行check,对于每一个九宫格,其实也可以用二重循环check,只是当前的坐标需要进行一下转换,即第i个九宫格里面的9个坐标怎么用i,j表示呢?
     board[3 * (i/3)+(j/3)][3*(i%3)+j%3]

class Solution {
private:
    bool check(int* arr, int val) {
        if (val < 0) return true;
        if (arr[val] == 1) return false;
        arr[val] = 1;
        return true;
    }
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        //sanity chck
        if (board.size() == 0 || board[0].size() == 0) return false;
        //use 3 int array to do the check
        int col[10] = {0};
        int row[10] = {0};
        int small[10] = {0};
        for (int i = 0; i < 9; i++) { //ith row
            memset(col, 0, sizeof(col));
            memset(row, 0, sizeof(row));
            memset(small, 0, sizeof(small));
            for (int j = 0; j < 9; j++) { //jth col
                if (!check(col, board[j][i]-'0') || !check(row, board[i][j]-'0') ||!check(small, board[3 * (i/3)+(j/3)][3*(i%3)+j%3]-'0'))
                return false;
            }
        }
        return true;
    }
};

Leetcode 37:  sudoku solver
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.
题目大意: 填 sudoku 9*9的格子, 每一行、列不能有重复的数字,并且3*3的格子也不能有重复的数字。
解题思路: 对于这种有一定规律地填格子的游戏,完全可以用DFS的思想来做, 因为每一个格子的填法都相同,每填完一个格子就对相应的行和列以及相应的3*3的格子进行检查,如果是valid,那么就对下一个格子进行填写。
M1: worst one!!!
先填列,再填行,如果一个格子不需要填, 跳过这个格子再填下一个。
这样填法,对于一些case,可能会出现超时,比如说,一个9*9的格子里面已经有很多已经填好的数字了,只有仅仅的几列都空格,这种情况跟worst case的时间复杂度是相同的。
M2: 如何避免这种case呢???
可以做一个preprocessing,把需要填的empty格子都记录下来,不用一个一个地去遍历original的每一个格子,只要直接对事先记录的empty的格子进行填写,如何判断填完了呢? 可以利用一个计数器 cur, 表示目前已经填好的empty的格子。 每填完一个cur+1dfs到下一层,base case 是当cur == empty.size()的时候就可以return了,代表已经填好了。 这样做对于大多数的case可以节省很多时间。

class Solution {
private:
//check if the sudoku is valid
    bool isValid(vector<vector<char>> &board, int i, int j) {
        for (int k = 0; k < 9; k++) {
            //check if there is a same num in ith row
            if (k != j && board[i][k] == board[i][j]) {
                return false;
            }
            //check if there is a same num in jth column
            if (k != i && board[k][j] == board[i][j]) {
                return false;
            }
        }
        //check if there are dup nums in a 3*3 CELL
        for (int m = i/3 * 3; m < i/3 * 3 + 3; m++) {
            for (int n = j/3 *3; n < j/3 * 3 + 3; n++) {
                if ((i != m || j != n) && (board[m][n] == board[i][j])) {
                    return false;
                }
            }
        }
        return true;
    }
    //fill the sudoku, fill column first then fill row
    bool helper(vector<vector<char>> &board,  vector<int> empty, int cur) {
        //base case
        if (cur == empty.size()) return true;
        int index = empty[cur];
        int row = index/9;
        int col = index%9;
        for (int k = 1; k <= 9; k++) {
            board[row][col] = k + '0';
            if (isValid(board, row, col))  {
                if (helper(board,empty, cur+1)) {
                    return true;
                }
            }
            board[row][col] = '.';
        }
        return false;
    }
public:
    void solveSudoku(vector<vector<char>>& board) {
        //sanity check
        if (board.size() == 0 || board.size() != 9 || board[0].size() != 9) return;
        vector<int> empty;
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') {
                    empty.push_back(i * 9 + j);
                }
            }
        }
        helper(board, empty, 0);
    }
};

class Solution {
private:
//check if the sudoku is valid
    bool isValid(vector<vector<char>> &board, int i, int j) {
        for (int k = 0; k < 9; k++) {
            //check if there is a same num in ith row
            if (k != j && board[i][k] == board[i][j]) {
                return false;
            }
            //check if there is a same num in jth column
            if (k != i && board[k][j] == board[i][j]) {
                return false;
            }
        }
        //check if there are dup nums in a 3*3 CELL
        for (int m = i/3 * 3; m < i/3 * 3 + 3; m++) {
            for (int n = j/3 *3; n < j/3 * 3 + 3; n++) {
                if ((i != m || j != n) && (board[m][n] == board[i][j])) {
                    return false;
                }
            }
        }
        return true;
    }
    //fill the sudoku, fill column first then fill row
    bool helper(vector<vector<char>> &board,  vector<int> empty, int cur) {
        //base case
        if (cur == empty.size()) return true;
        int index = empty[cur];
        int row = index/9;
        int col = index%9;
        for (int k = 1; k <= 9; k++) {
            board[row][col] = k + '0';
            if (isValid(board, row, col))  {
                if (helper(board,empty, cur+1)) {
                    return true;
                }
            }
            board[row][col] = '.';
        }
        return false;
    }
public:
    void solveSudoku(vector<vector<char>>& board) {
        //sanity check
        if (board.size() == 0 || board.size() != 9 || board[0].size() != 9) return;
        vector<int> empty;
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') {
                    empty.push_back(i * 9 + j);
                }
            }
        }
        helper(board, empty, 0);
    }
};

Leetcode 51: N-Queen
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
题目大意: N皇后问题,使每行每列还有对角线上放置的皇后不会相互攻击。
解题思路: DFS, 对于DFS树,总共有N层,代表有这么多行,每一层一共是N种Q的放置方法,放置在该行的不同列上,但是这个是可以剪枝的DFS,因为并不是每种放置都是Valid的。 所以在每放一个Q时应该去检查这样是否valid,如果valid就DFS往下一层放置。
对于Valid函数,最直观的写法是,检测该列上是否有Q,检测左对角线是否有Q,检测右对角线是否有Q,但是这个函数的时间复杂度是O(n)。有没有能一次性判断是否valid的方法,使得时间复杂度是O(1)呢??有! 可以利用一个vector记录每一行上Q所在的列,然后遍历所有的层,如果vector上记录的该层Q所在列和此列正好相同就说明冲突,或者对角线x轴差距和y轴差距相同,则对角线冲突。  if(state[i] == col || abs(row - i) == abs(col - state[i]))。因为DFS里面判断valid的次数很多,优化VALID函数可以很大程度上优化运行效率。

class Solution {
private:
    bool isValid(vector<int> state, int col, int row) {
        for(int i = 0; i < row; i++)//只需要判断row前面的行,因为后面的行还没有放置
            if(state[i] == col || abs(row - i) == abs(col - state[i]))
                return false;
        return true;
       // return true;
    }
    void helper(int n, vector<string> solu, vector<vector<string>> &rst, int row, vector<int> state) {
        //base case
        if (row == n) {
            rst.push_back(solu);
            return;
        }
        for (int i = 0; i < n; i++) {
            solu[row][i] = 'Q';
            state[row] = i;
            if (isValid(state, i, row))
                helper(n, solu, rst, row+1,state);
            solu[row][i] = '.';
            state[row] = -1;
        }
    }
public:
//use dfs to solve
    vector<vector<string> > solveNQueens(int n) {
        string s = "";
        vector<string> solu;
        vector<vector<string>> rst;
        vector<int> state(n, -1); //state to keep track of the position of Queen in each row
        //sanity check
        if(n == 0) return rst;
        for (int i = 0; i < n; i++) {
            s = "";
            for (int j = 0; j < n; j++) {
                s += '.';
            }
            solu.push_back(s);
        }
       // rst.push_back(solu);
        helper(n, solu, rst,0, state);
        return rst;
    }
};

 

No comments:

Post a Comment