您的位置:首页 > 其它

数独求解(回溯)

2013-10-13 23:13 225 查看
/**
* Sudoku.cpp
* @author arhaiyun
* Date : 2013-10-13
*
**/

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <algorithm>
using namespace std;
int map[9][9];

bool isPlace(int index)
{
int row = index / 9;
int col = index % 9;
//[1].检查每一行是否有相同数字
for(int i = 0; i < 9; i++)
{
if(map[row][i] == map[row][col] && i != col)
{
return false;
}
}

//[2].检查每一列是否有相同数字
for(int i = 0; i < 9; i++)
{
if(map[i][col] == map[row][col] && i != row)
{
return false;
}
}

//[3].检查每个小格中是否有相同的数字
int _row = row / 3 * 3;
int _col = col / 3 * 3;

for(int i = _row; i < _row + 3; i++)
{
for(int j = _col; j < _col + 3; j++)
{
if(map[i][j] == map[row][col] && (i != row || j != col))
{
return false;
}
}
}

return true;
}

void back_trace(int index)
{
if(index == 81)
{
fstream out("out.txt",ios::out);
for(int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
out << map[i][j] <<" ";
}
out << endl;
}
return ;
}

int row = index / 9;
int col = index % 9;

if(map[row][col] == 0)
{
for(int i = 1; i <= 9; i++)
{
map[row][col] = i;
if(isPlace(index))
{
back_trace(index + 1);
}
}
map[row][col] = 0;
}
else
{
back_trace(index + 1);
}
}

int main()
{
fstream in("in.txt", ios::in);

for(int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
in >> map[i][j];
}
}

back_trace(0);

system("pause");
return 0;
}

/*
input:
5 3 0 0 7 0 0 0 0
6 0 0 1 9 5 0 0 0
0 9 8 0 0 0 0 6 0
8 0 0 0 6 0 0 0 3
4 0 0 8 0 3 0 0 1
7 0 0 0 2 0 0 0 6
0 6 0 0 0 0 2 8 0
0 0 0 4 1 9 0 0 5
0 0 0 0 8 0 0 7 9
output:
5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 5 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9
*/


类的形式实现

#include <assert.h>
#include <iostream>
using namespace std;

// 求解数独程序

typedef int GridData[9][9]; // 9x9的网格数据

// 网格类
// 每个网格由3x3个块,每个块有3x3个0~9的数字
// 记录网格数据和9个块中已经有多少被填写了
class Grid
{
GridData _data;
int _filledBlockCount;// 已经填写完成的块的数量
public:
// 构建函数,复制网格数据,并将块完成计数置0
Grid(GridData data) : _filledBlockCount(0)
{
memcpy(_data, data, sizeof(int)*81);
}

// 构建函数,复制另一网格中的网格数据和块完成计数
Grid(const Grid& other) : _filledBlockCount(other._filledBlockCount)
{
memcpy(_data, other._data, sizeof(int)*81);
}

// 取得网格中x, y处的数字
int get(int x, int y) const
{
int res = _data[y][x];
return _data[y][x];
}

// 设置网格中x, y处的数字
void set(int x, int y, int digit)
{
_data[y][x] = digit;
}

// 取得完成块计数
int getFilledBlockCount()
{
return _filledBlockCount;
}

// 增加完成块计数
void incrFilledBlockCount()
{
_filledBlockCount ++;
}

// 减少完成块计数
void decrFilledBlockCount()
{
_filledBlockCount --;
}
};

// 数独类
class Sudoku
{
// 格子类
// 记录一个格子的位置
struct Cell
{
int x;
int y;
};

// 判断一个x, y处的格子是否可以填入数字digit
static bool canFill(const Grid& grid, int x, int y, int digit)
{
// 判断横竖两行是否已经存在数字digit
// 如果存在,就不能填充
for (int i=0; i<9; i++)
{
if(grid.get(x, i) == digit || grid.get(i, y) == digit)
return false;
}
return true;
}

// 块类
// 一个块包括3x3个数字
// 填充完成后,一个块应该包括1-9这9个数字
class Block
{
int _x, _y;            // 块在网格中的位置
Grid& _grid;           // 网格的引用,从中取得块中的数字
Cell _blankCells[9];   // 在块中的空白格子
int  _unfillDigits[9]; // 在块中还未填入的数字
int  _unfillCount;     // 未填入的数字的个数
public:
Block(int x, int y, Grid& grid) : _x(x), _y(y), _grid(grid)
{
Cell* pCell = _blankCells;
// 先将所有的数字都视为没有填入的数字
for(int i = 0; i < 9; i++)
{
_unfillDigits[i] = i+1;
}

// 遍历3x3的块
for(int y=0; y<3; y++)
{
for(int x=0; x<3; x++)
{
int d = grid.get(_x + x, _y + y); // 从网格中取得块中x, y处的数字
if (d == 0)
{                     // 如果数字为0,表示没有填充
pCell->x = _x + x;            // 记录在空白格子列表中
pCell->y = _y + y;
pCell++;
}
else
{                           // 否则是已经填入的数字
_unfillDigits[d-1] = 0;      // 将该数字从未填入数字中清楚
}
}
}

// 把已清楚的未填入数字挤掉,并统计未填入数字数量
_unfillCount = 0;
extractUnfill();
}

// 求解一个块
bool solve()
{
return solve(_grid, _blankCells, _unfillDigits, _unfillCount);
}
private:
// 求解网格grid中的一个块
// blank是当前的未填入格子
// unfills是未填入数
// num是未填入数的数量,也是未填入格子数量
bool solve(Grid& grid, Cell* blank, int* unfills, int num) {
if(num == 0) {                             // 如果没有未填入格子,表示已经填入所有数字,即填完一个块
grid.incrFilledBlockCount();           // 将网格的完成块计数加一
if (grid.getFilledBlockCount() == 9)   // 如果填入的块的数量已经达到9块
return true;                       // 问题已经解决,返回真
Sudoku sdk(grid);                      // 如果还有没有填完的块,把当前的网格视为一个新的数独
if(sdk.solve()) {                      // 对这个新的数独进行求解,如果求解成功
sdk.copyGridTo(grid);              // 这也是前一个数独的解,将解复制到原来的数独中
return true;                       // 成功,返回真
}
else {                                 // 否则
grid.decrFilledBlockCount();       // 废弃掉上次的填完数字的块,完成块计数减一
return false;
}
}
// 如果还有未填入数字,开始填入工作

// 取得空白格子的位置
int x = blank->x;
int y = blank->y;
// 依次尝试填入未填数字
for (int i=0; i<num; i++)
{
int digit = unfills[i];
if (Sudoku::canFill(grid, x, y, digit))
{ // 如果未填数字能够填入这个位置
grid.set(x, y, digit);                // 填入
int restUnfills[9];
getOtherDigits(restUnfills, unfills, num, i);   // 将其它数字放入一个新的未填入列表restUnfills
if (solve(grid, blank+1, restUnfills, num-1))
{ // 从下一个空白格子开始,将剩下的未填入数据视为一个新的块进行求解
return true;                                // 如果能够解出来,则求出了整个问题的解
}
else
{
grid.set(x, y, 0);                          // 无法解出,说明填入这个数字是不可以的,清除这个数字,开始尝试下一个数字
}
}
}
return false; // 如果所有的数字顺序都不正确,则说明前面的块填写不正确,返回到前一个块进行求解
}
// 将all中除excepIdx位置外的数字放到rest中
void getOtherDigits(int* rest, int* all, int num, int exceptIdx) {
for (int i=0; i<num; i++) {
if(i < exceptIdx)       // 如果在exceptIdx前面的数字,直接放过去
rest[i] = all[i];
else if(i > exceptIdx)
rest[i-1] = all[i]; // 后面的数字向前移一位
}
}
// 挤掉_unfillDigits中的已填入数(对应位置的数字被清0)
void extractUnfill()
{
int * p, * q;
p = q = _unfillDigits; // p, q指针指向_unfillDigits数组
while (q < _unfillDigits+9)
{ // q遍历数组
if(*q != 0)
{             // q指向的数组如果不为0
*p = *q;              // 复制到p指向的位置
p++;                  // p只在*q不为0的情况下移动
_unfillCount++;       // 对未填入数计数
}
q++;
}
}
};

Grid _grid;
public:
Sudoku(GridData initData) : _grid(initData)
{

}
Sudoku(const Grid& grid) :  _grid(grid)
{

}

// 将网格数据复制到网格grid中
void copyGridTo(Grid& grid)
{
grid = _grid;
}

// 求解
bool solve()
{
// 获得已完成的块的数量
int filledCount = _grid.getFilledBlockCount();

// 计算第一个有未填入数字的块的位置
int bx = filledCount % 3;
int by = filledCount / 3;

// 从这个块开始进行求解
Block block(bx * 3, by * 3, _grid);
return block.solve();
}
// 打印块的数据
void printGrid()
{
printGrid(_grid);
}
private:
void printGrid(const Grid& grid)
{
for (int y=0; y<9; y++)
{
for(int x=0; x<9; x++)
{
cout << grid.get(x, y) << " ";
if(x % 3 == 2) cout << " ";
}
cout << endl;
if(y % 3 == 2) cout << endl;
}
}
};

int main()
{
GridData initData = {
5,3,0,  0,7,0,  0,0,0,
6,0,0,  1,9,5,  0,0,0,
0,9,8,  0,0,0,  0,6,0,

8,0,0,  0,6,0,  0,0,3,
4,0,0,  8,0,3,  0,0,1,
7,0,0,  0,2,0,  0,0,6,

0,6,0,  0,0,0,  2,8,0,
0,0,0,  4,1,9,  0,0,5,
0,0,0,  0,8,0,  0,7,9
};
Sudoku sdk(initData);
if(sdk.solve())
{
sdk.printGrid();
}
else
{
cout << "该数独无解" << endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: