数独的生成以及解答--回溯算法c++附详细代码

一,数独的规则

  1. 横向上9个数字满足1-9不重复;
  2. 竖向上9个数字满足1-9不重复;
  3. 将大网格拆分为9个3*3的小网格,每个小网格内同样满足1-9不重复

二,生成数独的思路

首先准备一个空的数独,从第一个格子开始,按照数独的规则,把1-9依次试探性的填入,如果合法,就填入.如果不合法就判断下一个数合不合法,这样,一直到把第81个格子填完,就生成了一个完整的数独,但是因为这样生成的数独是一个固定的,所以,现在我们要随机把它打乱,根据数独的特性,在同一个小九宫格中的行和行之间交换位置,列与列之间交换位置,数独依然成立,打个比方,第0行和第2行,交换位置,第0列和第2列交换位置,数独依然成立.

  这样随机打乱位置后,再根据要求的难度,随机挖空其中的一些格子,这样就生成了我们需要的数独题目了!

bool isValid(int row, int col, int num, vector>&board)
    {
        //检查行有没有重复的,如果有返回faulse
        for (int i = 0; i < board.size(); i++)
        {
            if (num == board[row][i]-'0')
            {
                return false;
            }
        }
        //检查列有没有重复的,如果有返回faulse
        for (int i = 0; i < board.size(); i++)
        {
            if (num == board[i][col]-'0')return false;
        }
        //检查数字所在的方块有没有重复的,如果有返回faulse
        int startrow = row / 3 * 3;
        int startcol = col / 3 * 3;
        int endrow = startrow + 3;
        int endcol = startcol + 3;
        for (int i = startrow; i < endrow; i++)
        {
            for (int j = startcol; j < endcol; j++)
            {
                if (num == board[i][j]-'0')return false;
            }
        }
        return true;

    }
    void swapCol(int m, int n, vector>& board)
    {
        vectortemp(board.size(), ' ');
        for (int i = 0; i < board.size(); i++)
        {
            temp[i] = board[i][m];
            board[i][m] = board[i][n];
            board[i][n] = temp[i];
        }
    }

    void selectBlank(int nums, vector>& board)
    {
        srand(time(NULL));
        while (nums)
        {
            int row = rand() % 9;
            int col = rand() % 9;
            if (board[row][col] != '.')
            {
                board[row][col] = '.';
                nums--;
            }
        }
    }

    void create(int blank, vector>& board)
    {
        //小九宫格中的行和列交换,有以下9种交换方式
        int choice[9][2] = { {0,1},{0,2},{1,2},{3,4},{3,5},{4,5},{6,7},{6,8},{7,8} };
        srand(time(NULL));//设置动态种子
        for (int j = 0; j < 3; j++)//J代表交换次数,也可以不用这个循环,就交换一次
        {
            int i = rand() % 9;//取0-8之间的随机值
            board[choice[i][0]].swap(board[choice[i][1]]);//随机交换两行
            swapCol(choice[i][0], choice[i][1], board);//随机交换两列
        }
        selectBlank(blank, board);//根据难度可以选择不同数量的空格
        
    }

三,计算数独的思路

根据提供的数独,从第一个方格开始遍历,先判断是不是空,如果为空就把1-9依次试探性的填入,然后根据数独的规则,判断填入的数合不合法,如果合法就填入.然后递归遍历下一个方格,如果判断出下一个方格填入失败,说明现在填入的数是错的,需要回溯,然后把这个填入的数改为空,然后继续试探别的数字,如果所有数字试完都不行,那么就返回false,回溯到上一个数字.

这样直到把所有空格都遍历完成.

完整代码为:

// two.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include 
#include 
#include 
#include 
#include 
#include
using namespace std;



class sudoku
{
public:
    bool active = false;
    sudoku(vector>&board)
    {
       // printBoard(board);
         active = dfs(board, 0);
                    
    }
    void printBoard(vector>&board)
    {
        cout << " |---|---|---|---|---|---|---|---|---| " << endl;
        for (int i = 0; i < board.size(); i++)
        {
            for (int j = 0; j < board.size(); j++)
            {
                cout << " | " << board[i][j];
            }
            cout << endl;
            cout << "  ---|---|---|---|---|---|---|---|---| " << endl;
        }
    }
    bool isValid(int row, int col, int num, vector>&board)
    {
        //检查行有没有重复的,如果有返回faulse
        for (int i = 0; i < board.size(); i++)
        {
            if (num == board[row][i]-'0')
            {
                return false;
            }
        }
        //检查列有没有重复的,如果有返回faulse
        for (int i = 0; i < board.size(); i++)
        {
            if (num == board[i][col]-'0')return false;
        }
        //检查数字所在的方块有没有重复的,如果有返回faulse
        int startrow = row / 3 * 3;
        int startcol = col / 3 * 3;
        int endrow = startrow + 3;
        int endcol = startcol + 3;
        for (int i = startrow; i < endrow; i++)
        {
            for (int j = startcol; j < endcol; j++)
            {
                if (num == board[i][j]-'0')return false;
            }
        }
        return true;

    }
    void swapCol(int m, int n, vector>& board)
    {
        vectortemp(board.size(), ' ');
        for (int i = 0; i < board.size(); i++)
        {
            temp[i] = board[i][m];
            board[i][m] = board[i][n];
            board[i][n] = temp[i];
        }
    }

    void selectBlank(int nums, vector>& board)
    {
        srand(time(NULL));
        while (nums)
        {
            int row = rand() % 9;
            int col = rand() % 9;
            if (board[row][col] != '.')
            {
                board[row][col] = '.';
                nums--;
            }
        }
    }

    void create(int blank, vector>& board)
    {
        //小九宫格中的行和列交换,有以下9种交换方式
        int choice[9][2] = { {0,1},{0,2},{1,2},{3,4},{3,5},{4,5},{6,7},{6,8},{7,8} };
        srand(time(NULL));//设置动态种子
        for (int j = 0; j < 3; j++)//J代表交换次数,也可以不用这个循环,就交换一次
        {
            int i = rand() % 9;//取0-8之间的随机值
            board[choice[i][0]].swap(board[choice[i][1]]);//随机交换两行
            swapCol(choice[i][0], choice[i][1], board);//随机交换两列
        }
        selectBlank(blank, board);//根据难度可以选择不同数量的空格
        
    }


    bool dfs(vector>&board, int start)//从0开始依次遍历81个格子,计算此数独
    {
        if (start == 81)//start=81,说明已经成功解出数独
        {
            return true;
        }
        else
        {
            bool ok = false;
            int row = start / 9;//根据此时方格的序号,计算出此方格的行和列
            int col = start % 9;
            if (board[row][col] == '.')
            {
                for (int i = 1; i <= 9; i++)
                {
                    if (isValid(row, col, i, board))//从1-9依次放入空格,并判断是否合法
                    {
                        board[row][col] = i + '0';//如果有数字合法,就写入该数字的字符
                        ok = dfs(board, start + 1);//判断此方格的下一个方格是否成功写入
                        if (!ok)//如果它的下一个方格是不合法的,说明它现在填入的数,不是正确的解,需回溯
                        {
                            board[row][col] = '.';//回溯
                        }
                    }
                }
            }
            else
            {
                ok = dfs(board, start + 1);
            }
            return ok;
        }
       
        
    }
};



int main()
{
    /*vector>board = { vector{'5','3','.','.','7','.','.','.','.'},
                                  vector{'6','.','.','1','9','5','.','.','.'},
                                  vector{'.','9','8','.','.','.','.','6','.'},
                                  vector{'8','.','.','.','6','.','.','.','3'}, 
                                  vector{'4','.','.','8','.','3','.','.','1'}, 
                                  vector{'7','.','.','.','2','.','.','.','6'}, 
                                  vector{'.','6','.','.','.','.','2','8','.'}, 
                                  vector{'.','.','.','4','1','9','.','.','5'}, 
                                  vector{'.','.','.','.','8','.','.','7','9'}, };*/
     
    vector>blank(9, vector(9,'.'));//创建一个9*9的空二维数组
  
    sudoku s(blank);
    s.create(50,blank);//创建50个空格的数独
    if (s.active)
    {
        cout << "创建的数独为:" << endl;
        s.printBoard(blank);
    }
    else
        cout << "数独表不合法,没有解!" << endl;
    s.dfs(blank, 0);//解数独函数
    if (s.active)
    {
        cout << "此数独的解是:" << endl;
        s.printBoard(blank);
    }
    else
        cout << "解题失败!" << endl;

    return 0;
}

数独的生成以及解答--回溯算法c++附详细代码_第1张图片

 

你可能感兴趣的:(数据结构和算法,算法,c++,数据结构)