题目链接如下:https://projecteuler.net/problem=96.
Euler project 96 实际是要求解一个数独的问题。 高中的时候唱唱利用小课的时间做这样的题目,感觉很有意思。不过有一些题目实在太难,可能性太多,最后也不得不放弃。最近在学C++编程,觉得蛮有意思,所以写了一下。
大体思路如下:
1)首先找到一个尚未确定位置,并根据其所在的行,列,和3*3的矩阵的数据来确定其可能的取值;
2)依次验证所选的取值,并更新9*9矩阵,判断下一个位置;
3)重复执行前面两步,直到找不到尚未确定的位置,并返回结果。
这种思路很简单,但有两个易错点:递归的找下一个位置,记得回溯;到达终止条件记得return;
#include "iostream"
#include "fstream"
#include "set"
using namespace::std;
#define N 50
#define NUM 9
int aProblem[N][NUM][NUM];
void fnReadFile()
{
ifstream file;
file.open("p096_sudoku.txt");
char ch;
for(int i=0;i<N;i++)
{
while(1)
{
file.get(ch);
if('0'<=ch&&ch<='9')
{
file.get(ch);
break;
}
}
for(int j=0;j<NUM;j++)
{
file.get(ch);
for(int k=0;k<NUM;k++)
{
file.get(ch);
aProblem[i][j][k]=ch-'0';
}
}
}
file.close();
}
bool find(int arr[][NUM],int& i,int& j)
{
bool flag=0;
for(int ii=0;ii<NUM;ii++)
{
for(int jj=0;jj<NUM;jj++)
{
if(arr[ii][jj]==0)
{
i=ii;
j=jj;
flag=1;
break;
}
}
if(flag==1)
{
break;
}
}
return flag;
}
void fnSudo(int arr[][NUM],int& re)
{
int i;
int j;
if(!find(arr,i,j))
{
re=arr[0][0]*100+arr[0][1]*10+arr[0][2];
return;
}
if(find(arr,i,j))
{
set<int> S;
S.clear();
for(int k=0;k<NUM;k++)
{
if(arr[i][k]>0)
{
S.insert (arr[i][k]);
}
}
for(int k=0;k<NUM;k++)
{
if(arr[k][j]>0)
{
S.insert (arr[k][j]);
}
}
int kk=i/3;
int tt=j/3;
for(int k=kk*3;k<(kk+1)*3;k++)
{
for(int t=tt*3;t<(tt+1)*3;t++)
{
if(arr[k][t]>0)
{
S.insert (arr[k][t]);
}
}
}
for(int k=1;k<NUM+1;k++)
{
int oldsize=S.size ();
S.insert(k);
if(S.size()>oldsize)
{
arr[i][j]=k;
fnSudo(arr,re);
arr[i][j]=0;
}
}
}
}
int main()
{
fnReadFile();
int arr[NUM][NUM];
int sum=0;
int re;
for(int i=0;i<N;i++)
{
for(int j=0;j<NUM;j++)
{
for(int k=0;k<NUM;k++)
{
arr[j][k]=aProblem[i][j][k];
}
}
fnSudo(arr,re);
sum+=re;
}
cout<<"the sum of all problems is:"<<sum<<endl;
ofstream file1;
file1.open("result.txt");
file1<<sum<<endl;
file1.close();
return 1;
}
我还有一种没有实现的想法,用启发式求解。把9*9矩阵作为系统的状态,依次确定每一个状态的后置状态,并跟新OPEN 和 CLOSED 矩阵,直到找到一个所有的数字都不为空状态。
这种想法的难点在于,对结点进行分层,每一层代表该层确定的位置,如第一层是初始矩阵,第2层代表确定的是第1个位置的所有可能的取值后变换的矩阵。