数独是一个很多人都喜欢的游戏。对于这样的问题,我比较喜欢的解决方案是写一个程序来解决这些问题。不过这些问题显然是那种需要回溯需要优化的问题。游戏规则我就不罗嗦了,言归正传。
首先是解决数独问题的算法。程序输入,一个9*9的输入矩阵,有数字的地方就是指定的数字,没有数字的地方输入为0。算法的主要思想如下:
1、创建一个堆栈trackStack,用来保存用程序填入的格子的信息。
2、查找所有的数字为0的地方,察看这些地方所有的可能选择的数量,找到最小数量的那个格子。
3、如果找到的最小数量为0的话,表明没有数字可以填写了。从trackStack中弹出一项,重新填写这个格子可能的一个数据。重复2;
4、如果找到的最小数量不是0的话,在格子中填入最小的可能数据,将填入的格子的信息压入堆栈trackStack。如果还有格子为空,重复2;
5、结束。
下面是完整的程序:
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace sodoku
{
class DataItem
{
public int i;
public int j;
public int value ;
}
class Program
{
static bool bContinue = true ;
static Stack<DataItem> trackStack = new Stack<DataItem>();
static void OutputResult(int[][] iData)
{
for(int i = 0 ; i < 9 ; i++ )
{
Console.WriteLine();
for (int j = 0; j < 9; j++)
{
Console.Write("{0}\t", iData[i][j]);
}
}
}
static bool CheckValidInput(int[][] iData)
{
return true;
}
static int SearchMinOptions(int[][] iData)
{
int iMaxCount = 0;
int iRow = 0, iColumn = 0;
int iMinNum = 0;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (iData[i][j] == 0)
{
int[] iFlag = new int[10];
for (int m = 0; m < 10; m++)
{
iFlag[m] = 0;
}
for (int k = 0; k < 9; k++)
{
iFlag[iData[i][k]] = 1;
iFlag[iData[k][j]] = 1;
iFlag[iData[i / 3 * 3+ k / 3][j / 3 * 3+ k % 3]] = 1;
}
int iCount = 0;
int iCurrMinNum = 0;
for (int m = 1; m < 10; m++)
{
if (iFlag[m] == 1)
iCount++;
if (iFlag[m] == 0 && iCurrMinNum == 0)
{
iCurrMinNum = m;
}
}
if (iCount > iMaxCount)
{
iRow = i;
iColumn = j;
iMaxCount = iCount;
iMinNum = iCurrMinNum;
}
}
}
}
if (iMinNum == 0)
{
//OutputResult(iData);
//check whether finished
if( CheckFinished(iData) )
{
Console.WriteLine( "Result:" ) ;
OutputResult( iData) ;
bContinue = false ;
return 0 ;
}
else
{
do
{
if (trackStack.Count == 0)
{
Console.WriteLine("Error!");
bContinue = false;
return 0;
}
DataItem item = trackStack.Pop() ;
int[] iFlag = new int[10];
for (int m = 0; m < 10; m++)
{
iFlag[m] = 0;
}
for (int k = 0; k < 9; k++)
{
iFlag[iData[item.i][k]] = 1;
iFlag[iData[k][item.j]] = 1;
iFlag[iData[item.i / 3 * 3+ k / 3][item.j / 3 * 3+ k % 3]] = 1;
}
for (int m = item.value + 1; m < 10; m++)
{
if (iFlag[m] == 0 && m > item.value )
{
iData[item.i][item.j] = m ;
item.value = m ;
trackStack.Push( item ) ;
return SearchMinOptions(iData);
}
}
}while(true ) ;
}
}
Console.WriteLine();
Console.WriteLine("X:{0},Y:{1},Num:{2}", iRow, iColumn,iMinNum);
DataItem item2 = new DataItem();
item2.i = iRow;
item2.j = iColumn;
item2.value = iMinNum;
trackStack.Push(item2);
iData[iRow][iColumn] = iMinNum;
return 0;
}
static bool CheckFinished(int[][] iData)
{
bool bFinished = true;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (iData[i][j] == 0)
{
bFinished = false;
break;
}
}
}
return bFinished;
}
static void Main(string[] args)
{
string[] strAll = System.IO.File.ReadAllLines(@"D:\Test\sodoku\example.txt");
int[][] iData = new int[9][] ;
for (int i = 0; i < 9; i++)
{
iData[i] = new int[9];
}
int iIndex = 0;
foreach (string strLine in strAll)
{
string[] strData = strLine.Split("\t".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
if (strData.Length != 9)
{
continue;
}
for( int i = 0 ; i < 9 ; i ++ )
{
iData[iIndex][i] = Convert.ToInt32(strData[i]) ;
}
iIndex ++ ;
if( iIndex == 9 )
{
break ;
}
}
if( iIndex != 9 )
{
Console.WriteLine( "Error reading file,must input 9 lines valid data!" ) ;
return ;
}
OutputResult( iData ) ;
CheckValidInput(iData);
do
{
SearchMinOptions(iData);
//OutputResult(iData);
//Console.ReadLine() ;
}
while (bContinue);
Console.ReadLine();
}
}
}
下面是一个输入的例子:
0 6 0 0 0 5 2 0 4
0 2 0 0 1 6 0 0 3
0 5 3 0 0 4 0 0 0
2 0 0 0 7 1 9 0 8
1 0 0 9 0 0 7 4 0
4 0 9 0 6 0 0 0 0
0 1 2 6 0 0 8 0 9
0 4 7 8 2 0 1 0 5
0 0 0 0 5 0 4 0 0
OK,上面的是我们解决数独问题的一个方法。接下来,我们需要讨论一下如何生成一个数独游戏,在生成数独游戏的过程中,我们首先需要做一些思考。对于8皇后问题,我想很多人都是知道的。其实对于我们的这个问题,其实和8皇后问题,有很大的相似的地方。仔细观察我们的数独游戏的规则,我们可以发现,如果只看一个数字,例如看2,其他的数字我们全部去掉话,这个问题不就是一个变形的8皇后问题吗?而且,我们自己观察那1到9的9个数字,其实是1-9还是A-I是没有什么差别的,这9个数字之间是相互对等的。也就是说,我们求解的目标是,一个皇后,她攻击的范围是和她同一行或者同一列或者在同一个九宫,现在有9*9的格子,里面需要放9个皇后,皇后之间不能相互攻击。我们需要计算出所有可能的皇后的解决方案。得到这些解决方案的列表以后,我们一共有9种颜色的皇后,每一种颜色的皇后都只能从刚才的得到的解决方案的列表中找到一个方案,而这9个方案放到一个相同的9*9的格子里面的时候,不能有任何的重叠。得到了这9个方案,分别将这9个方案对应一个数字。然后随机的从里面去掉一些数字。一个数独游戏就产生了。
下面是代码,希望大家喜欢,有问题给我留言,或者邮件lujianping@china.com.cn:
using System;
using System.Collections.Generic;
using System.Text;
namespace sdCreator
{
class Program
{
static void OutputResult(int[][] iData)
{
for (int i = 0; i < 9; i++)
{
Console.WriteLine();
for (int j = 0; j < 9; j++)
{
Console.Write("{0}\t", iData[i][j]);
}
}
}
class POINT
{
public int i;
public int j;
}
static void OutputResult(Stack<POINT> track)
{
foreach (POINT pnt in track)
{
Console.Write("{0},{1};", pnt.i, pnt.j);
}
Console.WriteLine();
}
static bool ConflictData(int i, int j, Stack<POINT> track)
{
foreach (POINT pnt in track)
{
if (pnt.i == i || pnt.j == j || (pnt.i / 3 == i / 3 && pnt.j / 3 == j / 3))
{
return true;
}
}
return false;
}
static List<Stack<POINT>> opt = new List<Stack<POINT>>();
static void CalcSoduCount()
{
int iCount = 0;
Stack<POINT> track = new Stack<POINT>();
int iCurrentLine = 0;
while (true)
{
bool bFind = false;
for (int j = 0; j < 9; j++)
{
if (!ConflictData(iCurrentLine , j, track))
{
POINT pnt = new POINT();
pnt.i = iCurrentLine;
pnt.j = j;
track.Push(pnt);
bFind = true;
iCurrentLine++;
}
}
if (bFind == false)
{
//revert
do
{
POINT pnt = track.Pop();
bFind = false;
for (int j = pnt.j + 1; j < 9; j++)
{
if (!ConflictData(pnt.i, j, track))
{
pnt.j = j;
track.Push(pnt);
bFind = true;
iCurrentLine = pnt.i + 1 ;
break;
}
}
if (bFind == true)
{
break;
}
}
while (true);
}
else
{
if (track.Count == 9)
{
//Console.WriteLine("Find one Result");
//OutputResult(track);
Stack<POINT> newStack = new Stack<POINT>();
foreach (POINT pnt in track)
{
POINT newPnt = new POINT();
newPnt.i = pnt.i;
newPnt.j = pnt.j;
newStack.Push(newPnt);
}
opt.Add(newStack);
iCount++;
do
{
if (track.Count == 0)
{
Console.WriteLine("Total Count:{0}" , iCount );
return;
}
POINT pnt = track.Pop();
bFind = false;
for (int j = pnt.j + 1; j < 9; j++)
{
if (!ConflictData(pnt.i, j, track))
{
pnt.j = j;
track.Push(pnt);
bFind = true;
iCurrentLine = pnt.i + 1;
break;
}
}
if (bFind == true)
{
break;
}
}
while (true);
}
}
}
OutputResult(track);
}
static bool AddOneResult(int[][] iData, Stack<POINT> track , int iValue)
{
foreach (POINT pnt in track)
{
if (iData[pnt.i][pnt.j] != 0)
{
return false;
}
}
foreach (POINT pnt in track)
{
iData[pnt.i][pnt.j] = iValue;
}
return true;
}
static void SearchOneSolution()
{
int[][] iData = new int[9][];
for (int i = 0; i < 9; i++)
{
iData[i] = new int[9];
}
for (int i = 0; i < 9; i++)
{
for( int j = 0 ; j < 9 ; j ++ )
{
iData[i][j] = 0 ;
}
}
int iStart = 1000 ;
for (int i = 0; i < 9; i++)
{
for ( int j = iStart ; j < opt.Count ; j ++ )
{
if( AddOneResult( iData , opt[j] , i ) )
{
iStart = j + 1 ;
break ;
}
}
}
OutputResult( iData ) ;
}
static void Main(string[] args)
{
CalcSoduCount();
SearchOneSolution();
Console.ReadLine();
}
}
}