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

      比较容易想到的是用回溯法,从第一个格子开始到最后一格,每个格子由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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值