DLX解 各种规模数独

QQ;1423173783   email:1423173783@qq.com

// 本代码用DLX算法解决K*K*K*K数独,特点是覆盖了前K*K*K*K列,战斗就解决了,把战斗浓缩在前K*K*K*K列。缺点是用了new 调试不方便,new的数组不好观测
//解题填空顺序存储在O[]中      舞池本来应该用双向十字链表做材料,本代码用二维数组模拟
//本代码虽然可以做规模不同的数独(自然包括9*9),但随着K的变大U,D,R,L数组大小要变,(K*K*K*K*3+1)*3+1是几位数 new中前面的100000就要改为10的几次方
//事实上,K=5就做不了,会有abort错误,堆allocation出错
#include<iostream>
#include<cstdlib>
#include<time.h>
#include<fstream>
using namespace std;
#define K  4
fstream  out_stream;
int node[K*K+1][K*K+1];
long time1,time2;
int O[K*K*K*K+1],S[K*K*K*K+1],a[K*K*K*K*K*K+1][K*K*K*K*3+1],O_sub=1;// O[]依次存储覆盖每一列用的行号,行号隐藏了哪一个格子填了哪一个候选数
int *U=new int[(K*K*K*K*K*K+1)*10000+(K*K*K*K*3+1)*3+1];              //S[82]存放前81列的候选数可能数,对应于每个格子的候选数
int *D=new int[(K*K*K*K*K*K+1)*10000+(K*K*K*K*3+1)*3+1];              //a[][]用来表示跳舞的舞池  U[]  D[]  R[]   L[]  是舞池的设置
int *R=new int[(K*K*K*K*K*K+1)*10000+(K*K*K*K*3+1)*3+1];
int *L=new int[(K*K*K*K*K*K+1)*10000+(K*K*K*K*3+1)*3+1];
void remove(int c);         //用来覆盖列
void resume(int c);         //用来恢复列  
bool DFS(int k);            //每次优先覆盖候选数可能数最少的列
int room(int row,int col);   // 给定行号,列号,求宫号
void init(int start[K*K*K*K+1]);    //预先处理:布置公共舞池,对于特定的舞会(特定的数独题)还要事先做些准备工作
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%10000==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%10000==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<=K*K*K*K;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]%10000==0)
  {
     int pos=(i/10000-1)/(K*K)+1,k1;//1<=pos<=81
     k1=i/10000-K*K*(pos-1);
     O[k]=i/10000;
     subtraction(pos,k1);
     for(int j=R[i];j%10000>0 && j%10000<K*K*K*K*3+1;j=R[j])//j!=i改为j!=0 && j!=1又改为j>1000  又改为j!=1000 && j!=2000
     remove(j%10000);
     if(DFS(k+1)) return (true);
     for( int j=L[L[i]];j!=i;j=L[j])  
     resume(j%10000);
     add(pos,k1);
  }
 }
 resume(c);
 return(false);
}
int room(int row,int col)
{
 return((row-1)/K*K+1+(col-1)/K);
}

void init(int start[K*K*K*K+1])
{
    int row,col;
 memset(U,0,((K*K*K*K*K*K+1)*10000+(K*K*K*K*3+1)*3+1)*4);
 memset(D,0,((K*K*K*K*K*K+1)*10000+(K*K*K*K*3+1)*3+1)*4);
 memset(R,0,((K*K*K*K*K*K+1)*10000+(K*K*K*K*3+1)*3+1)*4);
 memset(L,0,((K*K*K*K*K*K+1)*10000+(K*K*K*K*3+1)*3+1)*4);
 for(col=0;col<=K*K*K*K*3;col++)
 {
  L[col]=col-1;
  R[col]=col+1;
 }
 L[0]=K*K*K*K*3;   R[K*K*K*K*3]=0;
 for(int i=1;i<=K*K*K*K;i++)
  S[i]=K*K;
 for( row=1;row<=K*K*K*K*K*K;row++)
 {
  int k=(row-1)/(K*K)+1;
  int row1=(k-1)/(K*K)+1,col1=(k%(K*K)==0)?K*K:(k%(K*K));
  int acol1,acol2,acol3;
     acol1=K*K*(row1-1)+col1;
  acol2=K*K*(K*K+col1-1)+row1;
  acol3=K*K*(K*K*2+room(row1,col1)-1)+(row1-(room(row1,col1)-1)/K*K-1)*K+col1-(room(row1,col1)-1)%K*K;
  a[row][acol1]=1;  a[row][acol2]=1;  a[row][acol3]=1; 
 }
    for(col=1;col<=K*K*K*K*3;col++)
 {
  int tmp;
  row=1;
  while(a[row][col]==0)
  { row++; } ;
  tmp=row; ;
  D[col]=10000*row+col;
  U[10000*row+col]=col;
  for(int i=row+1;i<=row+K*K-1;i++)//i<=729改为row+8  实验证明耗时降了1ms
   if(a[i][col]==0) continue;
   else
   {
    D[10000*tmp+col]=10000*i+col;
    U[10000*i+col]=10000*tmp+col;
    tmp=i;
      }
        D[10000*tmp+col]=col;
  U[col]=10000*tmp+col;
 }
 for(row=1;row<=K*K*K*K*K*K;row+=K*K)   //for循环内的内容经过替换  耗时减少大概0.5ms  不小心把原来for循环中的内容删掉了
 {
  int row0,col0,rom0,col1,col2,col3;
  col1=(row-1)/(K*K)+1;
  row0=(col1-1)/(K*K)+1;
  col0=col1-K*K*(row0-1);
  rom0=room(row0,col0);
  col2=K*K*(K*K+col0-1)+row0;
  col3=K*K*(K*K*2+rom0-1)+(row0-row_room(rom0))*K+(col0-col_room(rom0)+1);
  for(int i=row;i<=row+K*K-1;i++)
  {
   R[i*10000]=i*10000+col1;        L[i*10000+col1]=i*10000;
   R[i*10000+col1]=i*10000+col2;   L[i*10000+col2]=i*10000+col1;
   R[i*10000+col2]=i*10000+col3;   L[i*10000+col3]=i*10000+col2;
   R[i*10000+col3]=i*10000;        L[i*10000]=i*10000+col3;
  }
 }
 for(int i=1;i<=K*K*K*K;i++)
  if(start[i]!=0)
  {
            int row1=(i-1)/(K*K)+1,col1=(i%(K*K)==0)?K*K:(i%(K*K));
   int acol1,acol2,acol3;
   acol1=K*K*(row1-1)+col1;
   acol2=K*K*(K*K+col1-1)+row1;
   acol3=K*K*(K*K*2+room(row1,col1)-1)+(row1-(room(row1,col1)-1)/K*K-1)*K+col1-(room(row1,col1)-1)%K*K;
   O[O_sub++]=K*K*(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<=K*K;i++)
     {
    for(int j=1;j<=K*K;j++)
     {
   out_stream<<node[i][j]<<" ";
     if(j%K==0) out_stream<<"   ";
     }   
    out_stream<<endl;
    if(i%K==0)  out_stream<<endl;
     }
   out_stream<<endl<<endl;
}
void subtraction(int pos,int k)
{
     int row=(pos-1)/(K*K)+1,col=(pos%(K*K)==0)? K*K:(pos%(K*K));
     int rom=room(row,col),scol1,scol2,scol3;
     scol1=K*K*(row-1)+1;  scol2=K*K*(K*K+col-1)+1;  scol3=K*K*(K*K*2+rom-1)+1;
     for(int j=scol1;j<=scol1+K*K-1;j++)
     {
      int m=D[j];//后来加的,发现j按原来代码j一下子变得很大,就出了循环 ,改过后for循环的代码看起来也更易理解
      if(D[j]==j)  continue;  //这一列以前被remove掉,不该处理的不处理,该处理的一定处理
      S[j]--;
      while(L[m]/10000-(L[m]/10000-1)/(K*K)*K*K!=k)  m=D[m];
      L[m]+=(K*K*K*K*3+1);R[L[m]]=m;L[L[m]]=R[R[m]];   R[R[R[m]]]=L[m];
     }
     for(int j=scol2;j<=scol2+K*K-1;j++)
     {
      int m=D[j];
      if(D[j]==j)  continue;
      while(L[L[m]]/10000-(L[L[m]]/10000-1)/(K*K)*K*K!=k) 
       m=D[m];
      if(L[m]%10000!=K*K*row-1+col)  S[L[m]%10000]--;
      L[L[m]]+=(K*K*K*K*3+1);R[L[L[m]]]=L[m];L[L[L[m]]]=R[m];   R[R[m]]=L[L[m]];
     }
     for(int j=scol3;j<=scol3+K*K-1;j++)
     {
      int m=D[j];
      if(D[j]==j)  continue;
      while(R[m]/10000-(R[m]/10000-1)/(K*K)*K*K!=k)  m=D[m];
      if(((L[L[m]]/10000-1)/(K*K))/(K*K)+1!=row  && L[L[m]]%10000-(L[L[m]]%10000-1)/(K*K)*K*K!=col) S[L[L[m]]%10000]--;//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]+=(K*K*K*K*3+1);L[R[m]]=m;R[R[m]]=L[L[m]];   L[L[L[m]]]=R[m];                                       //这样改使得同一个候选数不会使某个格子的probable值减两次,如果减两次下次选择覆盖列就不是最优化,我预测这样改会降低时间复杂度。
     }                                                                                                 //实际上耗时反而增加了,改前1674ms  改后3636ms  我还是觉得随着数独规模的增大比如16*16,25*25数独,改后的代码时间复杂度会更好        
}                                                                                                            // 我做了16*16  代码中的选择速度要慢,可能规模还是不大没体现它的优势,可是16*16已经把内存几乎都用光了          
void add(int pos,int k)                                                                                                      
{
       int row=(pos-1)/(K*K)+1,col=(pos%(K*K)==0)? K*K:(pos%(K*K));
     int rom=room(row,col),scol1,scol2,scol3;
     scol1=K*K*(row-1)+1;  scol2=K*K*(K*K+col-1)+1;  scol3=K*K*(K*K*2+rom-1)+1;
     for(int j=scol3+K*K-1;j>=scol3;j--)
     {
      int m=D[j];
      if(D[j]==j)  continue;//不该恢复的不恢复,该恢复的一定恢复,不管处理了几次恢复到原样
      while(R[m]/10000-(R[m]/10000-1)/(K*K)*K*K!=k)  m=D[m];
      R[m]-=(K*K*K*K*3+1);L[R[m]]=m;R[R[m]]=L[L[m]];   L[L[L[m]]]=R[m];
      if(((L[L[m]]/10000-1)/(K*K))/(K*K)+1!=row  && L[L[m]]%10000-(L[L[m]]%10000-1)/(K*K)*K*K!=col) S[L[L[m]]%10000]++;
     }
     for(int j=scol2+K*K-1;j>=scol2;j--)
     {
      int m=D[j];
      if(D[j]==j)  continue;
      while(L[L[m]]/10000-(L[L[m]]/10000-1)/(K*K)*K*K!=k)  m=D[m];
      L[L[m]]-=(K*K*K*K*3+1);R[L[L[m]]]=L[m];L[L[L[m]]]=R[m];   R[R[m]]=L[L[m]];
      if(L[m]%10000!=K*K*row-1+col)  S[L[m]%10000]++;
     }
      for(int j=scol1+K*K-1;j>=scol1;j--)
     {
      int m=D[j];
      if(D[j]==j)  continue;
      while(L[m]/10000-(L[m]/10000-1)/(K*K)*K*K!=k)  m=D[m];
      L[m]-=(K*K*K*K*3+1);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)/K*K+1);
}
int col_room(int rom)
{
 return((rom-1)%K*K+1);
}
int main()
{
 int int_start[K*K*K*K+1];   //开始用于存储从文件中读取的题目,后来用来存储解题结果
 ifstream cin_stream;
 cin_stream.open("d:\\c++\\数独12\\question\\4.txt");
 for(int i=1;i<K*K*K*K+1;i++)
  cin_stream>>int_start[i];
 out_stream.open("d:\\c++\\数独12\\question\\question474.txt");
 time1=clock();
 init(int_start);
 DFS(O_sub);
 time2=clock();
 for(int i=1;i<=K*K*K*K;i++)
       int_start[(O[i]-1)/(K*K)+1]=O[i]-(O[i]-1)/(K*K)*K*K;
 for(int i=1;i<=K*K*K*K;i++)
 {
  int row,col;
  row=(i-1)/(K*K)+1;
  col=(i%(K*K)==0)?K*K:(i%(K*K));
  node[row][col]=int_start[i];
 }
 for(int i=1;i<=K*K*K*K;i++)
  out_stream<<((O[i]-1)/(K*K))/(K*K)+1<<"        "<<(O[i]-1)/(K*K)+1-((O[i]-1)/(K*K))/(K*K)*K*K<<"      "<<O[i]-(O[i]-1)/(K*K)*K*K<<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");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值