QQ 1423173783 EMAIL:1423173783@qq,com 欢迎提出不同意见。
// 本代码用DLX算法解决9*9数独,特点是覆盖了前81列,战斗就解决了,把战斗浓缩在前81列。缺点是用了new 调试不方便,new的数组不好观测
//解题填空顺序存储在O[]中 舞池本来应该用双向十字链表做材料,本代码用二维数组模拟
#include<iostream>
#include<cstdlib>
#include<time.h>
#include<fstream>
using namespace std;
fstream out_stream;
int node[10][10];
long time1,time2;
int O[82],S[82],a[730][244],O_sub=1;// O[]依次存储覆盖每一列用的行号,行号隐藏了哪一个格子填了哪一个候选数
int *U=new int[729967]; //S[82]存放前81列的候选数可能数,对应于每个格子的候选数
int *D=new int[729967]; //a[][]用来表示跳舞的舞池 U[] D[] R[] L[] 是舞池的设置
int *R=new int[729967];
int *L=new int[729967];
void remove(int c); //用来覆盖列
void resume(int c); //用来恢复列
bool DFS(int k); //每次优先覆盖候选数可能数最少的列
int room(int row,int col); // 给定行号,列号,求宫号
void init(int start[82]); //预先处理:布置公共舞池,对于特定的舞会(特定的数独题)还要事先做些准备工作
void print_all(); //把结果输出到相应的文件,即每个格子填什么数
void subtraction(int pos,int k); //减候选数 并取消某一格子对应的某一候选数 所对应的行的覆盖资格
void add(int pos,int k); //与上一函数相反
int row_room(int rom); //给定宫号,求宫的开始行号
int col_room(int rom); //给定宫号,求宫的开始列号
void remove(int c)
{
L[R[c]]=L[c];
R[L[c]]=R[c];
for(int i=D[c];i!=c;i=D[i])
for(int j=R[i];j!=i;j=R[j])
{
if(j%1000==0) continue;
U[D[j]]=U[j];
D[U[j]]=D[j];
//S[j%1000]--; 这里保留原来的痕迹 后来改动的原因是,减候选数只会会减前81列,原来不光subtraction会减remove也会减,remove不会减前81列候选数,这里删掉耗时肯定变少,S[244]-->S[82]空间消耗也变少,别小看耗时少了5ms
}
}
void resume(int c)
{
for(int i=U[c];i!=c;i=U[i])
for(int j=L[i];j!=i;j=L[j])
{
if(j%1000==0) continue;
U[D[j]]=j;
D[U[j]]=j;
//S[j%1000]++;
}
L[R[c]]=c;
R[L[c]]=c;
}
bool DFS(int k)
{
if(!R[0]) return(true);
int count=100,c;
for(int i=R[0];i<=81;i=R[i])//i改为i<=81
if(S[i]<count)
{
count=S[i];
c=i;
if(count==1) break;
}
remove(c);
for(int i=D[c];i!=c;i=D[i])
{
if(L[i]%1000==0)
{
int pos=(i/1000-1)/9+1,k1;//1<=pos<=81
k1=i/1000-9*(pos-1);
O[k]=i/1000;
subtraction(pos,k1);
for(int j=R[i];j%1000>0 && j%1000<244;j=R[j])//j!=i改为j!=0 && j!=1又改为j>1000 又改为j!=1000 && j!=2000
remove(j%1000);
if(DFS(k+1)) return (true);
for( int j=L[L[i]];j!=i;j=L[j])
resume(j%1000);
add(pos,k1);
}
}
resume(c);
return(false);
}
int room(int row,int col)
{
return((row-1)/3*3+1+(col-1)/3);
}
void init(int start[82])
{
int row,col;
memset(U,0,729967*4);
memset(D,0,729967*4);
memset(R,0,729967*4);
memset(L,0,729967*4);
for(col=0;col<=243;col++)
{
L[col]=col-1;
R[col]=col+1;
}
L[0]=243; R[243]=0;
for(int i=1;i<=81;i++)
S[i]=9;
for( row=1;row<=729;row++)
{
int k=(row-1)/9+1;
int row1=(k-1)/9+1,col1=(k%9==0)?9:(k%9);
int acol1,acol2,acol3;
acol1=9*(row1-1)+col1;
acol2=9*(9+col1-1)+row1;
acol3=9*(18+room(row1,col1)-1)+(row1-(room(row1,col1)-1)/3*3-1)*3+col1-((room(row1,col1)-1)%3*3);
a[row][acol1]=1; a[row][acol2]=1; a[row][acol3]=1;
}
for(col=1;col<=243;col++)
{
int tmp;
row=1;
while(a[row][col]==0)
{ row++; } ;
tmp=row; ;
D[col]=1000*row+col;
U[1000*row+col]=col;
for(int i=row+1;i<=row+8;i++)//i<=729改为row+8 实验证明耗时降了1ms
if(a[i][col]==0) continue;
else
{
D[1000*tmp+col]=1000*i+col;
U[1000*i+col]=1000*tmp+col;
tmp=i;
}
D[1000*tmp+col]=col;
U[col]=1000*tmp+col;
}
for(row=1;row<=729;row+=9) //for循环内的内容经过替换 耗时减少大概0.5ms 不小心把原来for循环中的内容删掉了
{
int row0,col0,rom0,col1,col2,col3;
col1=(row-1)/9+1;
row0=(col1-1)/9+1;
col0=col1-9*(row0-1);
rom0=room(row0,col0);
col2=9*(9+col0-1)+row0;
col3=9*(18+rom0-1)+(row0-row_room(rom0))*3+(col0-col_room(rom0)+1);
for(int i=row;i<=row+8;i++)
{
R[i*1000]=i*1000+col1; L[i*1000+col1]=i*1000;
R[i*1000+col1]=i*1000+col2; L[i*1000+col2]=i*1000+col1;
R[i*1000+col2]=i*1000+col3; L[i*1000+col3]=i*1000+col2;
R[i*1000+col3]=i*1000; L[i*1000]=i*1000+col3;
}
}
for(int i=1;i<=81;i++)
if(start[i]!=0)
{
int row1=(i-1)/9+1,col1=(i%9==0)?9:(i%9);
int acol1,acol2,acol3;
acol1=9*(row1-1)+col1;
acol2=9*(9+col1-1)+row1;
acol3=9*(18+room(row1,col1)-1)+(row1-(room(row1,col1)-1)/3*3-1)*3+col1-(room(row1,col1)-(room(row1,col1)-1)/3*3-1)*3;
O[O_sub++]=9*(i-1)+start[i];
subtraction(i,start[i]);//原来是放在三个remove后 DFS中也要做相应调整
remove(acol1);
remove(acol2);
remove(acol3);
}
}
void print_all()
{
out_stream<<"print storage"<<endl;
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
out_stream<<node[i][j]<<" ";
if(j%3==0) out_stream<<" ";
}
out_stream<<endl;
if(i%3==0) out_stream<<endl;
}
out_stream<<endl<<endl;
}
void subtraction(int pos,int k)
{
int row=(pos-1)/9+1,col=(pos%9==0)? 9:(pos%9);
int rom=room(row,col),scol1,scol2,scol3;
scol1=9*(row-1)+1; scol2=9*(9+col-1)+1; scol3=9*(18+rom-1)+1;
for(int j=scol1;j<=scol1+8;j++)
{
int m=D[j];//后来加的,发现j按原来代码j一下子变得很大,就出了循环 ,改过后for循环的代码看起来也更易理解
if(D[j]==j) continue; //这一列以前被remove掉,不该处理的不处理,该处理的一定处理
S[j]--;
while(L[m]/1000-(L[m]/1000-1)/9*9!=k) m=D[m];
L[m]+=244;R[L[m]]=m;L[L[m]]=R[R[m]]; R[R[R[m]]]=L[m];
}
for(int j=scol2;j<=scol2+8;j++)
{
int m=D[j];
if(D[j]==j) continue;
while(L[L[m]]/1000-(L[L[m]]/1000-1)/9*9!=k)
m=D[m];
if(L[m]%1000!=9*row-1+col) S[L[m]%1000]--;
L[L[m]]+=244;R[L[L[m]]]=L[m];L[L[L[m]]]=R[m]; R[R[m]]=L[L[m]];
}
for(int j=scol3;j<=scol3+8;j++)
{
int m=D[j];
if(D[j]==j) continue;
while(R[m]/1000-(R[m]/1000-1)/9*9!=k) m=D[m];
if(((L[L[m]]/1000-1)/9)/9+1!=row && L[L[m]]%1000-(L[L[m]]%1000-1)/9*9!=col) S[L[L[m]]%1000]--;//if(((L[L[m]]/1000-1)/9)/9+1!=row)改为if(((L[L[m]]/1000-1)/9)/9+1!=row && L[L[m]]%1000-(L[L[m]]%1000-1)/9*9!=col)
R[m]+=244;L[R[m]]=m;R[R[m]]=L[L[m]]; L[L[L[m]]]=R[m]; //这样改使得同一个候选数不会使某个格子的probable值减两次,如果减两次下次选择覆盖列就不是最优化,我预测这样改会降低时间复杂度。
} //实际上耗时反而增加了,改前1674ms 改后3636ms 我还是觉得随着数独规模的增大比如16*16,25*25数独,改后的代码时间复杂度会更好
}
void add(int pos,int k)
{
int row=(pos-1)/9+1,col=(pos%9==0)? 9:(pos%9);
int rom=room(row,col),scol1,scol2,scol3;
scol1=9*(row-1)+1; scol2=9*(9+col-1)+1; scol3=9*(18+rom-1)+1;
for(int j=scol3+8;j>=scol3;j--)
{
int m=D[j];
if(D[j]==j) continue;//不该恢复的不恢复,该恢复的一定恢复,不管处理了几次恢复到原样
while(R[m]/1000-(R[m]/1000-1)/9*9!=k) m=D[m];
R[m]-=244;L[R[m]]=m;R[R[m]]=L[L[m]]; L[L[L[m]]]=R[m];
if(((L[L[m]]/1000-1)/9)/9+1!=row && L[L[m]]%1000-(L[L[m]]%1000-1)/9*9!=col) S[L[L[m]]%1000]++;
}
for(int j=scol2+8;j>=scol2;j--)
{
int m=D[j];
if(D[j]==j) continue;
while(L[L[m]]/1000-(L[L[m]]/1000-1)/9*9!=k) m=D[m];
L[L[m]]-=244;R[L[L[m]]]=L[m];L[L[L[m]]]=R[m]; R[R[m]]=L[L[m]];
if(L[m]%1000!=9*row-1+col) S[L[m]%1000]++;
}
for(int j=scol1+8;j>=scol1;j--)
{
int m=D[j];
if(D[j]==j) continue;
while(L[m]/1000-(L[m]/1000-1)/9*9!=k) m=D[m];
L[m]-=244;R[L[m]]=m;L[L[m]]=R[R[m]]; R[R[R[m]]]=L[m];
S[j]++;
}
}
int row_room(int rom)
{
return((rom-1)/3*3+1);
}
int col_room(int rom)
{
return((rom-1)%3*3+1);
}
int main()
{
char char_start[82];
int int_start[82]; //开始用于存储从文件中读取的题目,后来用来存储解题结果
ifstream cin_stream;
cin_stream.open("d:\\c++\\数独12\\question\\1.txt");
cin_stream>>char_start;
for(int i=1;i<82;i++)
int_start[i]=char_start[i-1]-'0';
out_stream.open("d:\\c++\\数独12\\question\\question474.txt");
time1=clock();
init(int_start);
DFS(O_sub);
time2=clock();
for(int i=1;i<=81;i++)
int_start[(O[i]-1)/9+1]=O[i]-(O[i]-1)/9*9;
for(int i=1;i<=81;i++)
{
int row,col;
row=(i-1)/9+1;
col=(i%9==0)?9:(i%9);
node[row][col]=int_start[i];
}
for(int i=1;i<=81;i++)
out_stream<<((O[i]-1)/9)/9+1<<" "<<(O[i]-1)/9+1-((O[i]-1)/9)/9*9<<" "<<O[i]-(O[i]-1)/9*9<<endl;
out_stream<<endl;
out_stream<<O_sub-1<<endl;//输出的是初盘已经有的数的个数
print_all();
cout<<time2-time1<<"ms"<<endl;
delete[] U;
delete[] D;
delete[] R;
delete[] L;
system("pause");
}
for(int col1=1;col1<=81;col1++)//我把for循环的样子改成现在这种样子,耗时几乎yiyang沮丧随着数独规模的变大,我预计改后的样子耗时更少
{ // 这段代码图片前的局部程序的改进,思想是第一张图片。第二张图片也是一种思想,但我认为第一张图片算法耗时少
int col2,col3,row,col,rom;
row=(col1-1)/9+1; col=col1-9*(row-1); rom=room(row,col);
col2=81+9*(col-1)+row;
col3=162+9*(rom-1)+(row-(rom-1)/3*3-1)*3+col-(rom-1)%3*3;
D[col1]=(9*(col1-1)+1)*1000+col1; U[(9*(col1-1)+1)*1000+col1]=col1;
D[col2]=(9*(col1-1)+1)*1000+col2; U[(9*(col1-1)+1)*1000+col2]=col2;
D[col3]=(9*(col1-1)+1)*1000+col3; U[(9*(col1-1)+1)*1000+col3]=col3;
for(int i=9*(col1-1)+2;i<=9*(col1-1)+9;i++)
{
D[(i-1)*1000+col1]=i*1000+col1; U[i*1000+col1]=(i-1)*1000+col1;
D[(i-1)*1000+col2]=i*1000+col2; U[i*1000+col2]=(i-1)*1000+col2;
D[(i-1)*1000+col3]=i*1000+col3; U[i*1000+col3]=(i-1)*1000+col3;
}
D[(9*(col1-1)+9)*1000+col1]=col1; U[col1]=(9*(col1-1)+9)*1000+col1;
D[(9*(col1-1)+9)*1000+col2]=col2; U[col2]=(9*(col1-1)+9)*1000+col2;
D[(9*(col1-1)+9)*1000+col3]=col3; U[col3]=(9*(col1-1)+9)*1000+col3;
/*int tmp;
row=1;
while(a[row][col]==0)
{ row++; } ;
tmp=row; ;
D[col]=1000*row+col;
U[1000*row+col]=col;
for(int i=row+1;i<=row+8;i++)//i<=729改为row+8 实验证明耗时降了1ms
if(a[i][col]==0) continue;
else
{
D[1000*tmp+col]=1000*i+col;
U[1000*i+col]=1000*tmp+col;
tmp=i;
}
D[1000*tmp+col]=col;
U[col]=1000*tmp+col;*/
}