您的位置:首页 > 其它

数独(九宫格)的高效算法

2015-12-19 21:06 183 查看
      比较容易想到的是用回溯法,从第一个格子开始到最后一格,每个格子由1到9进行尝试,看能否填下去,不能就回头。思路简单,可是执行时间太长了。有没有更加高效的搜索算法了?当然有。

      我们仅需要改变搜索的顺序,或者说改变填数的方法。

     先将数字1分别填入1到9区域中,然后再将数字2填入1到9区域,重复该操作,直到所有空位都被填满为止。

     为什么这样就快了?因为先将一个数字成功填入9个区域的制约性,远远大于按顺序地从第一个填到最后一个格。假设第一行全是空的,在第一行的第一格填了数字1,然后假如在第二行填不下数字1,证明第一行的数字1填错了,这时通过回溯纠正它,最坏需要9!次。

但如果是一个个区域地填入数字1,当第二个1填不下时,马上就能回溯到上一个1去纠正它的位置了。

具体C语言源程序如下:

#include<stdio.h>

#include<string.h>

int quyu[4][4]={{0,0,0,0},

                        {0,1,2,3},

                        {0,4,5,6},

                        {0,7,8,9}};

int a[10][10],hang[10][10],lie[10][10],grid[10][10],sum;

int p[10][2]={{0,0},{1,1},{1,4},{1,7},{4,1},{4,4},{4,7},{7,1},{7,4},{7,7}};

//p[i][0]和p[i][1]数组记录第i个区域的左上角的横纵坐标

void init()

{

 int i,j,k;

 memset(hang,0,sizeof(hang));

 memset(lie,0,sizeof(lie));

 memset(grid,0,sizeof(grid));

 sum=0; 

 for(i=1;i<=9;i++)

 for(j=1;j<=9;j++) 

 {

   scanf("%d",&k);

   a[i][j]=k;

   if(k)

  {

    hang[i][k]=1;    //第i行填入k了

    lie[j][k]=1;       //第j列填入k了

    grid[quyu[(i-1)/3+1][(j-1)/3+1]][k]=1;  //  (i , j)所在区域填入k了

    sum++;

   }

 }

}

void outt()

{

 int i,j;

 for(i=1;i<=9;i++)

 {

  for(j=1;j<=8;j++) printf("%d ",a[i][j]);

  printf("%d\n",a[i][9]);

 }

}

void solve(int k1,int k2)  //将数字k1填入区域k2

{

  int x,y,i,j;

  if(sum==81) {outt();return;}  //数独全填好了就输出

  if(grid[k2][k1])   //区域k2中已有数字k1了

  {

   if(k2<9) solve(k1,k2+1); //填下一个区域

   else solve(k1+1,1); //填下一个数字

  }

  x=p[k2][0]; y=p[k2][1];

  for(i=x;i<=x+2;i++)

for(j=y;j<=y+2;j++)

if(a[i][j]==0 && hang[i][k1]==0 && lie[j][k1]==0) //第(i,j)格为空,且可填入k1

{

a[i][j]=k1;

sum++;

hang[i][k1] = lie[j][k1] = 1;

if(k2<9) solve(k1,k2+1); //填下一个区域

else solve(k1+1,1); //填下一个数字

a[i][j]=0;

sum--;

hang[i][k1] = lie[j][k1] = 0;

}

}

int main()

{

 init();

 solve(1,1);  //将数字1从区域1开始填起

 return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: