原链接:http://yzmduncan.iteye.com/blog/1151695
//测试花了17.58h
#include <iostream>
#include<time.h>
#include<fstream>
using namespace std;
#define BLANKS 55
#define FAIL 20
int table[9][9],table_for_solve[9][9],solves=0;
fstream out_stream;
const int INF = 0x7fffffff;
const int NN = 350;
const int MM = 750;
int n,m; //列,行
int cntc[NN];
int L[NN*MM],R[NN*MM],U[NN*MM],D[NN*MM];
int C[NN*MM];
int head;
int mx[MM][NN];
int O[MM],idx,op;
int ans[10][10];
int map[10][10]={0,0,0,0,0,0,0,0,0,0,
0,8,0,0,0,0,0,0,0,0,
0,0,0,3,6,0,0,0,0,0,
0,0,7,0,0,9,0,2,0,0,
0,0,5,0,0,0,7,0,0,0,
0,0,0,0,0,4,5,7,0,0,
0,0,0,0,1,0,0,0,3,0,
0,0,0,1,0,0,0,0,6,8,
0,0,0,8,5,0,0,0,1,0,
0,0,9,0,0,0,0,4,0,0};
void shuffle(int arr[], int n);
bool test(int x, int y, int v);
bool put(int line, int index);
bool put_line(int line);
void DFS();
int check(int y,int x,int *mark) ; //求probable[y][x] 并且mark[]中为0的元素说明可以试探
int solve22();
bool create_game(int blanks);
void create_gameover();
void print_all(int k); //输出到文件
void copy(int a[9][9],int b[9][9]);
void remove(int);
void resume(int);
bool dfs();
bool build();
void print();
int test();
bool fun(int a[10]);
void shuffle(int arr[], int n)
{
int tmp, rd;
for(int i = 0; i < n; i++)
{
rd = rand() % 9;
tmp = arr[rd];
arr[rd] = arr[i];
arr[i] = tmp;
}
}
bool test(int x, int y, int v)
{
int _x = x / 3 * 3;
int _y = y / 3 * 3;
for(int i = _x; i < _x + 3; i++) //测试3 * 3矩阵内是否有重复的数
{
for(int j = _y; j < _y + 3; j++)
{
if(table[i][j] == v)
{
return false;
}
}
}
for(int i = 0; i < 9; i++) //测试横向、纵向是否有重复的数
{
if(table[x][i] == v || table[i][y] == v)
return false;
}
return true;
}
bool put(int line, int index)
{
if(index > 8)
return true;
int num[] = {1,2,3,4,5,6,7,8,9};
//打乱当前准备写入数字的前后顺序
shuffle(num, 9);
for(int i = 0; i < 9; i++)
//测试数字是否允许填入当前方格
if( test(line, index, num[i]) == true )
{
table[line][index] = num[i];
//填入成功则处理下一个方格
if( put(line, index + 1) == true )
{
return true;
}
}
table[line][index] = 0; //失败后复位
return false;
}
bool put_line(int line)
{
if(line > 8)
return true;
if( put(line, 0) == true )
//当前一行添入完成后,进入下一行再重复处理。
if( put_line(line + 1) == true )
return true;
for(int i = 0; i < 9; i++)
table[line][i] = 0;
return false;
}
void DFS()
{
int i,j,im=-1,jm,min=10;
int mark[10];
for(i=0;i<9;++i)
for(j=0;j<9;++j)
{
if(table_for_solve[i][j])
continue;
int c=check(i,j,mark);
if(c==0)
return;
if(c<min)
{
im=i;
jm=j;
min=c;
}
}
if(im==-1)
{
solves++;
if(solves==2)
throw(1); //如果解法不唯一,不会等到所有解都出来才结束运行, 保留下面的return又能确定是不是只有唯一解。
return;
}
check(im,jm,mark);
for(i=1;i<=9;++i)
if(mark[i]==0)
{
table_for_solve[im][jm]=i;
DFS();
}
table_for_solve[im][jm]=0;
}
int solve22()
{
try
{
DFS();
solves=0; //调试后发现
return(1);
}
catch(int)
{
solves=0; //调试后发现,solves是全局变量,以后solves越来越大永远不可能等于2
return(2);
}
}
int check(int y,int x,int *mark) //求probable[y][x]
{
int i,j,is,js,count=0;
for(i=1;i<=9;++i)
mark[i]=0;
for(i=0;i<9;++i)
mark[table_for_solve[y][i]]=1;
for(i=0;i<9;++i)
mark[table_for_solve[i][x]]=1;
is=y/3*3;
js=x/3*3;
for(i=0;i<3;++i)
for(j=0;j<3;++j)
mark[table_for_solve[is+i][js+j]]=1;
for(i=1;i<=9;++i)
if(mark[i]==0)
count++;
return count;
}
bool create_game(int blanks)
{
int i,k,row,col,tmp;
for( i=1;i<=blanks;i++)
{
int num=0;
do
{
do
{
k=rand()%81;
row=k/9;
col=k-9*row;
tmp=table[row][col];
}while(tmp==0);
table[row][col]=0;
copy(table_for_solve,table);
num++;
if(num==FAIL) return(false);
}while((solve22()==2)? table[row][col]=tmp : 0);
}
if(i==blanks+1) return (true);
}
void create_gameover()
{
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
table[i][j]=0;
for(int i = 0; i < 9; i++)
table[0][i] = i + 1;
shuffle(table[0], 9);
//从第二行开始添入数字
while(!put_line(1)) ;
}
void print_all(int k)
{
for(int i=1;i<=9;i++)
{
if(i%3==1) out_stream<<endl;
for(int j=1;j<=9;j++)
{
if(j%3==1) out_stream<<" ";
out_stream<<table[i-1][j-1];
}
out_stream<<endl;
}
out_stream<<endl<<endl;
}
void copy(int a[9][9],int b[9][9])
{
for(int i=0;i<=8;i++)
for(int j=0;j<=8;j++)
a[i][j]=b[i][j];
}
//删除列及其相应的行
void remove(int c)
{
int i,j;
L[R[c]] = L[c];
R[L[c]] = R[c];
for(i = D[c]; i != c; i = D[i])
{
for(j = R[i]; j != i; j = R[j])
{
U[D[j]] = U[j];
D[U[j]] = D[j];
cntc[C[j]]--;
}
}
}
//恢复列及其相应的行
void resume(int c)
{
int i,j;
R[L[c]] = c;
L[R[c]] = c;
for(i = D[c]; i != c; i = D[i])
{
for(j = R[i]; j != i; j = R[j])
{
U[D[j]] = j;
D[U[j]] = j;
cntc[C[j]]++;
}
}
}
bool dfs()
{
int i,j,c;
if(R[head] == head)
return true;
int min = INF;
for(i = R[head]; i != head; i = R[i])
{
if(cntc[i] < min)
{
min = cntc[i];
c = i;
}
}
remove(c);
for(i = D[c]; i != c; i = D[i])
{
//i是某点的序号,将该点所在行的行号保存
O[idx++] = (i-1)/n;
for(j = R[i]; j != i; j = R[j])
remove(C[j]);
if(dfs())
return true;
for(j = L[i]; j != i; j = L[j])
resume(C[j]);
idx--;
}
resume(c);
return false;
}
bool build()
{
int i,j,now,pre,first;
idx = head = 0;
for(i = 0; i < n; i++)
{
R[i] = i+1;
L[i+1] = i;
}
R[n] = 0;
L[0] = n;
//列双向链表
for(j = 1; j <= n; j++)
{
pre = j;
cntc[j] = 0;
for(i = 1; i <= m; i++)
{
if(mx[i][j])
{
cntc[j]++;
now = i*n+j;
C[now] = j;
D[pre] = now;
U[now] = pre;
pre = now;
}
}
U[j] = pre;
D[pre] = j;
if(cntc[j] == 0)
return false;
}
//行双向链表
for(i = 1; i <= m; i++)
{
pre = first = -1;
for(j = 1; j <= n; j++)
{
if(mx[i][j])
{
now = i*n+j;
if(pre == -1)
first = now;
else
{
R[pre] = now;
L[now] = pre;
}
pre = now;
}
}
if(first != -1)
{
R[pre] = first;
L[first] = pre;
}
}
return true;
}
void print()
{
int i,j;
for(i = 1; i <= 9; i++)
{
if((i-1)%3==0) out_stream<<endl;
for(j = 1; j <= 9; j++)
if((j-1)%3==0) out_stream<<" "<<ans[i][j]<<" ";
else out_stream<<ans[i][j]<<" ";//printf("%d",ans[i][j]);
out_stream<<endl;//printf("\n");
}
}
int test()
{
int i,j;
int x,y,k;
for(i = 0; i < idx; i++)
{
int r = O[i];
k = r%9;
if(k==0) k = 9;
int num = (r - k)/9 + 1;
y = num%9;
if(y == 0) y = 9;
x = (num-y)/9 + 1;
ans[x][y] = k;
}
int sum=0,aray[10];
for(int i=1;i<=9;i++)
{
sum=0;
for(int j=1;j<=9;j++)
{ sum+=ans[i][j]; aray[j]=ans[i][j];}
if(sum!=45) {cout<<"wrong"<<endl; return 0;}
if(!fun(aray)) return 0;
}
for( j=1;j<=9;j++)
{
sum=0;
for(i=1;i<=9;i++)
{ sum+=ans[i][j]; aray[i]=ans[i][j];}
if(sum!=45) {cout<<"wrong"<<endl; return 0;}
if(!fun(aray)) return 0;
}
for(int k=1;k<=9;k++)
{
sum=0;
int row=(k-1)/3*3+1,col=(k-1)%3*3+1,sub=1;
for(i=row;i<=row+2;i++)
for(j=col;j<=col+2;j++)
{ sum+=ans[i][j]; aray[sub++]=ans[i][j];}
if(sum!=45) {cout<<"wrong"<<endl; return 0;}
if(!fun(aray)) return 0;
}
return 1;
}
bool fun( int a[10])
{
int i,j;
for(i=1;i<=8;i++)
{
if(*(a+i)==0) continue;
for(j=i+1;j<=9;j++)
if(*(a+i)==*(a+j)) return (false);
}
return (true);
}
int main()
{
int i,j,k;
long time1,time2;
char s[10][10];
srand(time(0));
time1=clock();
out_stream.open("d:\\my documents\\visual studio 2010\\projects\\测试算法\\测试pure dlx(one form).txt");
for( op=1;op<=1000000;op++)
{
create_gameover();
while(!create_game(BLANKS))
create_gameover();
/*for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
if(map[i][j]) s[i][j]=48+map[i][j];
else s[i][j]='?';*/
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
if(table[i-1][j-1]) s[i][j]=48+table[i-1][j-1];
else s[i][j]='?';
memset(mx,0,sizeof(mx));
for(i = 1; i <= 9; i++)
{
for(j = 1; j <= 9; j++)
{
int t = (i-1)*9 + j;
if(s[i][j] == '?')
{
for(k = 1; k <= 9; k++)
{
mx[9*(t-1)+k][t] = 1; //81grid 每个小格只能放一个数字
mx[9*(t-1)+k][81+(i-1)*9+k] = 1; //9row 每行数字k只能出现一次
mx[9*(t-1)+k][162+(j-1)*9+k] = 1; //9col 每列数字k只能出现一次
mx[9*(t-1)+k][243+((i-1)/3*3+(j+2)/3-1)*9+k] = 1; //subgrid 每个3*3格子数字k只能出现一次
}
}
else
{
k = s[i][j] - '0';
mx[9*(t-1)+k][t] = 1; //81grid
mx[9*(t-1)+k][81+(i-1)*9+k] = 1; //9row
mx[9*(t-1)+k][162+(j-1)*9+k] = 1; //9col
mx[9*(t-1)+k][243+((i-1)/3*3+(j+2)/3-1)*9+k] = 1; //subgrid
}
}
}
n = 324;
m = 729;
build();
dfs();
if(test()) ;
else { out_stream<<op<<endl; print_all(1); print();}
idx=0;
}
time2=clock();
out_stream<<"用时"<<(time2-time1)/1000<<"s"<<(time2-time1)%1000<<"ms"<<endl;
out_stream.close();
return 0;
}