《算法的乐趣》中数独的求解
// Sudoku.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <set>
#include <iostream>
using namespace std;
#define SKD_ROW_LIMET 9
#define SKD_COL_LIMET SKD_ROW_LIMET
#define SKD_CELL_COUNT (SKD_COL_LIMET)*(SKD_ROW_LIMET)
// 数独单元格结构;
typedef struct stSDKCell
{
int num; // 单元格值;
bool fixed; // 是否固定;
set<int> candidators; // 候选列表;
stSDKCell()
{
num = 0;
fixed = false;
for ( int i = 1; i <= 9; i++ )
{
candidators.insert(i);
}
}
}SUDUKU_CELL;
// 数独整体结构;
typedef struct stSDKGame
{
SUDUKU_CELL cells[SKD_ROW_LIMET][SKD_COL_LIMET];
int fixedCount;
stSDKGame()
{
fixedCount = 0;
}
}SUDUKU_GAME;
int SkipFixedCell( SUDUKU_GAME *pGame, int sp ); // 找到下一个未填充单元;
void CopyGameState( SUDUKU_GAME *pGame, SUDUKU_GAME *pnew_state ); // 数独结构深拷贝;
void PrintSudokuGame( SUDUKU_GAME *pGame ); // 打印数独解;
void SwitchCellToFiexed( SUDUKU_GAME *pGame, int row, int col, int num ); // 数独单元赋值并锁定;
set<int> FindRelatedCells( int row, int col ); // 当前(row,col)的相关单元;
bool ExclusiveCorrelative( SUDUKU_GAME *pGame, int row, int col, int num ); // 调整候选值范围;
bool ProcessSinglesCandidature( SUDUKU_GAME *pGame, int row, int col, int num ); // 单元候选值唯一的处理;
bool SetCandidatorTofixed( SUDUKU_GAME *pGame, int row, int col, int num ); // 试探性对数独单元锁值;
// 找出数独解决方案;
void FindSudokuSolution( SUDUKU_GAME *pGame, int sp )
{
if ( pGame->fixedCount == SKD_CELL_COUNT )
{
cout << "Find result :" << endl;
PrintSudokuGame( pGame );
}
sp = SkipFixedCell( pGame, sp );
if ( sp >= SKD_CELL_COUNT )
{
return;
}
int row = sp / SKD_COL_LIMET;
int col = sp % SKD_COL_LIMET;
SUDUKU_CELL *curCell = &pGame->cells[row][col];
SUDUKU_GAME new_state;
for ( auto iter = curCell->candidators.begin(); iter != curCell->candidators.end(); ++iter )
{
CopyGameState( pGame, &new_state );
if ( SetCandidatorTofixed( &new_state, row, col, *iter ) )
{
FindSudokuSolution( &new_state, sp + 1 );
}
}
}
// 数独单元赋值并锁定;
void SwitchCellToFiexed( SUDUKU_GAME *pGame, int row, int col, int num )
{
SUDUKU_CELL& cell = pGame->cells[row][col];
cell.num = num;
cell.fixed = true;
}
// 当前(row,col)的相关单元;
set<int> FindRelatedCells( int row, int col )
{
set<int> cells;
// 相关行;
for ( int iCol = 0; iCol < SKD_COL_LIMET; iCol++ )
{
cells.insert( row * SKD_COL_LIMET + iCol );
}
// 相关列;
for ( int iRow = 0; iRow < SKD_ROW_LIMET; iRow++ )
{
cells.insert( iRow * SKD_COL_LIMET + col );
}
// 九宫格;
int iRowS = row / 3 * 3;
int iColS = col / 3 * 3;
for ( int iRow = iRowS; iRow < iRowS + 3; iRow++ )
{
for ( int iCol = iColS; iCol < iColS + 3; iCol++ )
{
cells.insert( iRow * SKD_COL_LIMET + iCol );
}
}
return cells;
}
// 调整候选值范围;
bool ExclusiveCorrelative( SUDUKU_GAME *pGame, int row, int col, int num )
{
set<int> cells = FindRelatedCells( row, col);
for ( auto iter = cells.begin(); iter != cells.end(); ++iter )
{
int iRow = *iter / SKD_COL_LIMET;
int iCol = *iter % SKD_COL_LIMET;
SUDUKU_CELL& cell = pGame->cells[iRow][iCol];
if ( cell.fixed )
{
continue;
}
else
{
auto valIter = cell.candidators.find( num );
if ( cell.candidators.end() != valIter )
{
cell.candidators.erase( valIter );
if ( cell.candidators.empty() )
{
return false;
}
}
}
}
return true;
}
// 单元候选值唯一的处理;
bool ProcessSinglesCandidature( SUDUKU_GAME *pGame, int row, int col, int num )
{
set<int> cells = FindRelatedCells( row, col);
for ( auto iter = cells.begin(); iter != cells.end(); ++iter )
{
int iRow = *iter / SKD_COL_LIMET;
int iCol = *iter % SKD_COL_LIMET;
SUDUKU_CELL& cell = pGame->cells[iRow][iCol];
if ( !cell.fixed && cell.candidators.size() == 1 )
{
int iNum = *( cell.candidators.begin() );
if ( !SetCandidatorTofixed( pGame, iRow, iCol, iNum ) )
{
return false;
}
}
}
return true;
}
// 试探性对数独单元锁值;
bool SetCandidatorTofixed( SUDUKU_GAME *pGame, int row, int col, int num )
{
SwitchCellToFiexed( pGame, row, col, num );
if ( !ExclusiveCorrelative( pGame,row,col,num ) )
{
return false;
}
if ( !ProcessSinglesCandidature( pGame, row, col, num ) )
{
return false;
}
pGame->fixedCount++;
return true;
}
// 打印数独解;
void PrintSudokuGame( SUDUKU_GAME *pGame )
{
for ( int i = 0; i < SKD_ROW_LIMET; ++i )
{
for ( int j = 0; j < SKD_COL_LIMET; ++j )
{
cout << pGame->cells[i][j].num << "\t";
}
cout << endl;
}
}
// 找到下一个未填充单元;
int SkipFixedCell( SUDUKU_GAME *pGame, int sp )
{
while ( sp < SKD_CELL_COUNT )
{
int row = sp / SKD_COL_LIMET;
int col = sp % SKD_COL_LIMET;
if ( !pGame->cells[row][col].fixed )
{
break;
}
else
{
sp++;
}
}
return sp;
}
// 数独结构深拷贝;
void CopyGameState( SUDUKU_GAME *pGame, SUDUKU_GAME *pnew_state )
{
pnew_state->fixedCount = pGame->fixedCount;
for ( int i = 0; i < SKD_ROW_LIMET; ++i )
{
for ( int j = 0; j < SKD_COL_LIMET; ++j )
{
pnew_state->cells[i][j] = pGame->cells[i][j];
}
}
}
// 初始化数独已知数据;
void InitSDK( SUDUKU_GAME *pGame, int* datas )
{
for ( int i = 0; i < SKD_ROW_LIMET; ++i )
{
for ( int j = 0; j < SKD_COL_LIMET; ++j )
{
int ps = j + i * SKD_COL_LIMET;
if ( datas[ps] != 0 )
{
pGame->cells[i][j].num = datas[ps];
pGame->cells[i][j].fixed = true;
pGame->fixedCount++;
}
else
{
set<int> cells = FindRelatedCells( i, j );
for ( auto iter = cells.begin(); iter != cells.end(); ++iter )
{
if ( datas[*iter] != 0 )
{
set<int>& tempSet = pGame->cells[i][j].candidators;
auto valIter = tempSet.find( datas[*iter] );
if ( valIter != tempSet.end() )
{
tempSet.erase( valIter );
if ( tempSet.empty() )
{
cout << "No result!" << endl;
}
}
}
}
}
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int datas[SKD_CELL_COUNT] = { 0 };
datas[0] = 8;
datas[11] = 3;
datas[12] = 6;
datas[19] = 7;
datas[22] = 9;
datas[24] = 2;
datas[28] = 5;
datas[32] = 7;
datas[40] = 4;
datas[41] = 5;
datas[42] = 7;
datas[48] = 1;
datas[52] = 3;
datas[56] = 1;
datas[61] = 6;
datas[62] = 8;
datas[65] = 8;
datas[66] = 5;
datas[70] = 1;
datas[73] = 9;
datas[78] = 4;
SUDUKU_GAME game;
InitSDK( &game, datas );
FindSudokuSolution( &game, 0 );
system("pause");
return 0;
}