zoukankan      html  css  js  c++  java
  • Backtracking_37. 解数独

    编写一个程序,通过已填充的空格来解决数独问题。

    一个数独的解法需遵循如下规则:

    数字 1-9 在每一行只能出现一次。
    数字 1-9 在每一列只能出现一次。
    数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
    空白格用 '.' 表示。

     

    Note:

    • 给定的数独序列只包含数字 1-9 和字符 '.' 。
    • 你可以假设给定的数独只有唯一解。
    • 给定数独永远是 9x9 形式的。

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/sudoku-solver


    思路:

    这是一道困难题,说实话做了很久0.0

    要用到回溯的算法,总的思想,可以暴力解决,每个格子都从1-9来填进去看看行不行,如果不行就换,行就往下不行再退回来

    当然我们不这么做,要排除掉之前就已经不能填的格子比如在【0,2】这个格子,在第0行上要排除5,3,7这三个数,横向有了再填肯定不行

    再排除纵向,有个8,再排除box范围,5,3,6,9所以最后能填的只有1,2,4三个数先填上1,然后看后一个格子,这个时候横向要多排除一个1,其他老样子排除

    再往后还是这样,一直到最后一个,看看行不行,不行了就回溯,假设第一个填1一直都不行,那就换2,还不行再换4......

    最后第一行找完了,那就跳到第二行,依次类推下去

    class Solution {
        //定义三个数组,分别用于记录行使用情况,列使用情况和九个小格子使用情况
        int [][] row;
        int [][] col;
        int [][] block;
        //定义N存储给定数组的长度
        int N;
        boolean solve = false;
        //定义第一个方法,主要用于初始化数据
        public void solveSudoku(char [][] board){
            N = board.length;
            row = new int[N][N];
            col = new int[N][N];
            block = new int[N][N];
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (board[i][j] != '.'){
                        //将写在board上的具体值转换为它应该加入的坐标值
                        int t = board[i][j] - '0' - 1;
                        writeDown(board,i,j,t);
                    }
                }
            }
            dfs(board,0,0);
        }
    
        private void dfs(char[][] board, int i, int j){
            int box_number = (i / 3) * 3 + j / 3;
            //如果当前遍历到的是.那就说明要写入了,否则就走下一步
            if (board[i][j] == '.'){
                //可能填入的值有1-9,注意此处的循环是0-8
                for (int k = 0; k < N; k++) {
                    //判断当前的数字是否已经用过,除非行、列、格子全部都为0否则就是重复了
                    if (row[i][k] + col[j][k] + block[box_number][k] != 0){
                        continue;
                    }
                    writeDown(board,i,j,k);
                    next(board,i,j);
                    if (!solve)
                    clear(board,i,j,k);
                }
            }else {
                next(board,i ,j);
            }
        }
    
        //走下一步
        private void next(char[][] board,int i,int j){
            //说明已经到了终点
           if (i == N - 1 && j == N - 1) solve = true;
            else {
                if (j == N - 1){
                    dfs(board,i + 1,0);
                }else {
                    dfs(board,i,j + 1);
                }
            }
        }
    
        //定义一个写入的函数
        private void writeDown(char[][] board, int i, int j, int t) {
            //将该位置有数字的坐标记录到对应的记录表上去,分别是对应行,对应列,对应格子
            //将坐标转为表格号
            int box_number = (i / 3) * 3 + j / 3;
            //用1表示对应位置有数字,也就是写过了,后面遍历的时候可以跳过这个数字
            row[i][t] = 1;
            col[j][t] = 1;
            block[box_number][t] = 1;
            //之前把值转换为了坐标值,现在要给他赋回去
            board[i][j] =(char) (t + '0' + 1);
        }
    
        //对应的还要有一个清除的回溯用的方法
        private void clear(char[][] board, int i, int j, int t) {
            //将该位置有数字的坐标记录到对应的记录表上去,分别是对应行,对应列,对应格子
            //将坐标转为表格号
            int box_number = (i / 3) * 3 + j / 3;
            //用1表示对应位置有数字,也就是写过了,后面遍历的时候可以跳过这个数字
            row[i][t] = 0;
            col[j][t] = 0;
            block[box_number][t] = 0;
            board[i][j] = '.';
        }
    }
  • 相关阅读:
    LDAP安装配置(windows)
    chrome postman插件手动安装
    mabatis insert into on duplicate key
    ZOJ 3641 <并查集+STL>
    ZOJ 3633 <rmq 重点在于转化>
    POJ 2817 状态DP 字符串找最多的重复
    POJ 2771 简单二分图匹配
    POJ 1149 最大流<建图> PIGS
    POJ 3692 二分图最大独立点集
    POJ 2239 简单的二分图求最大匹配
  • 原文地址:https://www.cnblogs.com/zzxisgod/p/13380078.html
Copyright © 2011-2022 走看看