算法杂谈--构造数独

数独描述:在9*9的格子内,填入1~9,要求每一行每一列不能有重复数,并且,每个格子所处的3*3格子内不得有重复数。

分析:这是一个典型的递归回溯算法,本例中用一个except[9][9][9]的数组来构造每个格子的禁忌表(就是该格子中不能填入的数)。用变量hs来表示此次填数是向前递归运算还是回溯运算。如果是向前递归,则计算该格子的禁忌表,如果是回溯,则将当前格子中的数加入禁忌表。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
//用于标记是否回溯
int hs=0;
//记录每一个格子的禁忌数字,即当前格子不能填入的数字
int except[9][9][9];
//保存结果
int res[9][9];

//随机获取一个值
int getRan()
{
	return rand()/(RAND_MAX+1.0)*9+1;
}

//判断随机的值是否在禁忌表
int inforb(int jg,int *fibd)
{
	if(fibd[jg-1]==jg)return 1;
	return 0;
}

//判断禁忌表是否已满,如果满则需要回溯
int is_full(int *fbid)
{
	int i;
	for(i=0;i<9;i++)
	{
		if(fbid[i]==0)return 0;
	}
	return 1;
}

//构造数独
void gouzao(int i,int j)
{
	if(j==9)
	{
		i++;
		j=0;
	}
	//当到第10行的时候递归结束
	if(i==9)return;
	//如果是回溯到当前位置的,则把该值填入禁忌表
	if(hs)
	{
		except[i][j][res[i][j]-1]=res[i][j];
		hs=0;
	}
	else
	{
		int k,m;
		//获取禁忌表
		bzero(except[i][j],sizeof(except[i][j]));
		for(k=0;k<j;k++)
			except[i][j][res[i][k]-1]=res[i][k];
		for(k=0;k<i;k++)
			except[i][j][res[k][j]-1]=res[k][j];
		for(k=3*(i/3);k<3*(i/3+1);k++)
		{
			int end=0;
			for(m=3*(j/3);m<3*(j/3+1);m++)
			{
				if(k==i&&m==j)
				{
					end=1;
					break;
				}
				except[i][j][res[k][m]-1]=res[k][m];
			}
			if(end)break;
		}
	}
	//禁忌表满,回溯
	if(is_full(except[i][j]))
	{
		hs=1;
		//由于要回溯了,当前的值清零
		res[i][j]=0;
		int hsi=i,hsj=j-1;
		if(hsj<0)
		{
			hsj=8;
			hsi--;
		}
		gouzao(hsi,hsj);
	}
	//禁忌表未满,随机获取值,并记录
	else{
		int ran;
repeat:
		ran=getRan();
		if(inforb(ran,except[i][j]))
			goto repeat;
		res[i][j]=ran;
		gouzao(i,j+1);
	}
}

int main()
{
	srand(time(NULL));
	gouzao(0,0);	
	int i,j;
	for(i=0;i<9;i++)
	{
		for(j=0;j<9;j++)
			printf("%d ",res[i][j]);
		putchar('\n');
	}
}


结果:




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值