数独求解(回溯)
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; } }
相关文章推荐
- cas单点登录整合spring security
- 各浏览器对页面外部资源加载的策略
- 程序开发使用地图API时需注意的问题
- 程序开发使用地图API时需注意的问题
- 深信服面经 2014 武汉
- oracle表空间
- 家喻户晓Android机器人是谁设计的?
- RTP协议(补充)
- 产品运营 招聘
- 扫描版pdf的修改编辑
- linux DHCP服务器配置,基于CentOS6.4
- 《国内首部基于JBPM5.4实战流程引擎开发(动态表单、模板引擎、公文管理系统)》
- 参数化查询
- Android有效解决加载大图片时内存溢出的问题
- 小猪的情绪
- 再谈使用Emit把Datatable转换为对象集合(List<T>)
- Android手势
- Ubuntu下Android开发:手机不能识别
- 流水线
- 在shell中使用expect+ssh登陆远程服务器