Leetcode数独

一道穷举法(backtracking)求数独的算法 http://oj.leetcode.com/problems/sudoku-solver/

之所以贴出这道题,是因为和之前的穷举法有些不一样,调得我晚饭都没吃上,看的对穷举法,准确来说是backtracking, 的理解还不是很深。

class Solution {
public:
    void solveSudoku(vector<vector<char> >& board) {
    	doSolveSudoku(board);  
    }

    void getCandidates(vector<vector<char> >& board, vector<char>& v, int i, int j){
    	int flag[10];
    	for(int k=1;k<10;k++) flag[k]=k;
    
    	for(int k=0;k<board.size();k++){
    		if(board[i][k]!='.'){ flag[board[i][k]-'0']=0;}
    		if(board[k][j]!='.') flag[board[k][j]-'0']=0;
    	}
    	for(int k1=(i/3)*3;k1<(i/3)*3+3;k1++){
    		for(int k2=(j/3)*3;k2<(j/3)*3+3;k2++){
    		    if(board[k1][k2]!='.') 
    			flag[board[k1][k2]-'0']=0;
    		}	
    	}
    	for(int k=1;k<10;k++)
           if(flag[k]!=0) v.push_back(k+'0');	
    }

    pair<int, int> findFirstEmpty(const vector< vector<char> >& board) {
        for (int i=0; i<9; ++i)
            for (int j=0; j<9; ++j)
                if (board[i][j] == '.') 
		    return make_pair(i, j);
        return make_pair(-1, -1);
    }

    bool doSolveSudoku(vector<vector<char> >& board){
        pair<int, int> pos=findFirstEmpty(board);  //先进行位置搜索
    	if(pos.first==board.size() || pos.first==-1 && pos.second==-1)
    		 return true;
	vector<char> candidates;
	candidates.clear();
	getCandidates(board, candidates, pos.first, pos.second);
	for(int k=0;k<candidates.size();k++){
		board[pos.first][pos.second]=candidates[k];
		if(doSolveSudoku(board))  //如果不需要回溯,则一直走到最后返回
			return true;
		board[pos.first][pos.second]='.';//如果需要回溯,则将当前位复原
	}
	return false;	
    }
};
这里与之前不同的是:

(1)不是每个位置都需要穷举,故进入递归调用前必须找到第一个目标位置!

(2)由于这里是通过引用返回值,board这里既是输入,也是输出,故要分清什么时候需要回溯。如果回溯,则需要将当前位置恢复原样;否则不需要。(之前一直没遇到这种情况,因为我都是在找到一个合法解就直接输出该解然后退出)。

(3)这里是二维数组的遍历,虽然没有之前一维遍历时熟悉的level,但是道理还是一样,需要枚举的位置。

下面附上别人写的一段代码。

class Solution {
public:
    // 返回第一个空白的位置,如果没找到就返回 (-1, -1)
    pair<int, int> findFirstEmpty(const vector< vector<char> >& board) {
        for (int i = 0; i < 9; ++i)
            for (int j = 0; j < 9; ++j)
                if (board[i][j] == '.') return make_pair(i, j);
        return make_pair(-1, -1);
    }

    // 检查连续的 9 个格子是否有效
    bool isValid(const vector<char>& vec) {
        vector<bool> occur(9, false);
        for (int i = 0; i < 9; ++i) {
            if (isdigit(vec[i])) {
                if (occur[vec[i]-'1']) return false;
                else occur[vec[i]-'1'] = true;
            }
        }
        return true;
    }

    // 检查往某个位置填入一个数之后整个 board 是否有效(只需要考虑当前行、
    // 当前列和所属的田字格)
    bool isValidBoard(const vector< vector<char> >& board, pair<int, int> pos) {
        // 检查当前行是否有效
        if (!isValid(board[pos.first])) return false;

        // 检查当前列是否有效
        vector<char> column(9);
        for (int i = 0; i < 9; ++i)
            column[i] = board[i][pos.second];
        if (!isValid(column)) return false;

        // 检查所在的田字格是否有效
        int block_row = pos.first / 3;
        int block_col = pos.second / 3;
        vector<char> block;
        for (int i = block_row * 3; i < block_row * 3 + 3; ++i)
            for (int j = block_col * 3; j < block_col * 3 + 3; ++j)
                block.push_back(board[i][j]);
        if (!isValid(block)) return false;

        // 如果以上都有效,则返回 true
        return true;
    }

    // 检查从当前局面开始是否能够得到最终合法有效的解
    bool solveSudoku(vector<vector<char> >& board) {
        // 如果没有找到空白的格子,说明已经填满了,成功返回
        pair<int, int> pos = findFirstEmpty(board);
        if (pos.first == -1 && pos.second == -1)
            return true;
        // 否则依次尝试往当前格子中填入数字 1-9,并判断能否得到可行的解
        for (int i = 1; i <= 9; ++i) {
            board[pos.first][pos.second] = i + '0';
            if (isValidBoard(board, pos) && solveSudoku(board))
                return true;
            // 恢复原样
            board[pos.first][pos.second] = '.';
        }
        return false;
    }
};




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值