数独(转)

本文介绍了一个使用C语言实现的数独游戏生成及解答程序。通过拉斯维加斯算法随机填充部分数字,并采用唯一解法和回溯法确保数独拥有唯一解。程序还支持用户选择难度级别进行挖空,提供了提示功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<conio.h>
#define LS 4 //拉斯维加斯算法计算的数目
int shudu[9][9]; //数独棋盘
int shudutemp[9][9],shudutemp1[9][9],shudutemp2[9][9]; //保存数度棋盘,以便恢复原状
int p,q; //记录回溯解数独的起始位置
int isok=0;
int a[81];//记录是否回溯到了最底层(即是否有解)

//初始化数独
void init()
{
int i,j;
for(i=0;i<9;i++)
for(j=0;j<9;j++)
shudu[i][j]=0;
}

//把一个9*9二维数组内的数据复制给另一个数组
void write(int a[][9],int b[][9])
{
int i,j;
for(i=0;i<9;i++)
for(j=0;j<9;j++)
b[i][j]=a[i][j];
}

//打印数独shudu
void print()
{
printf("┏━┯━┯━┳━┯━┯━┳━┯━┯━┓/n");
printf("┃%d │%d │%d ┃%d │%d │%d ┃%d │%d │%d ┃/n",shudu[0][0],shudu[0][1],shudu[0][2],shudu[0][3],shudu[0][4],shudu[0][5],shudu[0][6],shudu[0][7],shudu[0][8]);
printf("┠-┼-┼-╂-┼-┼-╂-┼-┼-┨/n");
printf("┃%d │%d │%d ┃%d │%d │%d ┃%d │%d │%d ┃/n",shudu[1][0],shudu[1][1],shudu[1][2],shudu[1][3],shudu[1][4],shudu[1][5],shudu[1][6],shudu[1][7],shudu[1][8]);
printf("┠-┼-┼-╂-┼-┼-╂-┼-┼-┨/n");
printf("┃%d │%d │%d ┃%d │%d │%d ┃%d │%d │%d ┃/n",shudu[2][0],shudu[2][1],shudu[2][2],shudu[2][3],shudu[2][4],shudu[2][5],shudu[2][6],shudu[2][7],shudu[2][8]);
printf("┣━┿━┿━╋━┿━┿━╋━┿━┿━┫/n");
printf("┃%d │%d │%d ┃%d │%d │%d ┃%d │%d │%d ┃/n",shudu[3][0],shudu[3][1],shudu[3][2],shudu[3][3],shudu[3][4],shudu[3][5],shudu[3][6],shudu[3][7],shudu[3][8]);
printf("┠-┼-┼-╂-┼-┼-╂-┼-┼-┨/n");
printf("┃%d │%d │%d ┃%d │%d │%d ┃%d │%d │%d ┃/n",shudu[4][0],shudu[4][1],shudu[4][2],shudu[4][3],shudu[4][4],shudu[4][5],shudu[4][6],shudu[4][7],shudu[4][8]);
printf("┠-┼-┼-╂-┼-┼-╂-┼-┼-┨/n");
printf("┃%d │%d │%d ┃%d │%d │%d ┃%d │%d │%d ┃/n",shudu[5][0],shudu[5][1],shudu[5][2],shudu[5][3],shudu[5][4],shudu[5][5],shudu[5][6],shudu[5][7],shudu[5][8]);
printf("┣━┿━┿━╋━┿━┿━╋━┿━┿━┫/n");
printf("┃%d │%d │%d ┃%d │%d │%d ┃%d │%d │%d ┃/n",shudu[6][0],shudu[6][1],shudu[6][2],shudu[6][3],shudu[6][4],shudu[6][5],shudu[6][6],shudu[6][7],shudu[6][8]);
printf("┠-┼-┼-╂-┼-┼-╂-┼-┼-┨/n");
printf("┃%d │%d │%d ┃%d │%d │%d ┃%d │%d │%d ┃/n",shudu[7][0],shudu[7][1],shudu[7][2],shudu[7][3],shudu[7][4],shudu[7][5],shudu[7][6],shudu[7][7],shudu[7][8]);
printf("┠-┼-┼-╂-┼-┼-╂-┼-┼-┨/n");
printf("┃%d │%d │%d ┃%d │%d │%d ┃%d │%d │%d ┃/n",shudu[8][0],shudu[8][1],shudu[8][2],shudu[8][3],shudu[8][4],shudu[8][5],shudu[8][6],shudu[8][7],shudu[8][8]);
printf("┗━┷━┷━┻━┷━┷━┻━┷━┷━┛/n");
}


//判断n能否填入shudu[i][j]位置
int check(int n,int i,int j)
{
int c,r,l,m;
//判断同一行中是否已填入此数字
for(c=0;c<9;c++)
if(n==shudu[i][c])
return 0;
//判断同一列中是否已填入此数字
for(r=0;r<9;r++)
if(n==shudu[r][j])
return 0;
//判断同一九宫格中是否已填入此数字
for(l=0;l<3;l++)
for(m=0;m<3;m++)
{
if(shudu[i/3*3+l][j/3*3+m]==n)
return 0;
}
return 1;
}

//拉斯维加斯算法,用于随机产生一些数字
int lasvgas( )
{
int i,j,m;
int n=0;
srand(time(0));

do{
//先选定一个位置
do{
i=rand()%9;
j=rand()%9;
}while(shudu[i][j]!=0);
//产生一个可以填入shudu[i][j]的随机数
do{
m=rand()%9+1;
}while(check(m,i,j)==0);
shudu[i][j]=m;
n++;
}while(n<LS);
return 1;
}
//解数独算法:唯一法
void single()
{
int i,j,n,k=0,p=0,t;
for(i=0;i<9;i++)
{
for(j=0;j<9;j++)
{
k=0;
if(shudu[i][j]==0)
{

for(n=1;n<=9;n++)
{

p=check(n,i,j);
if(p==1)//表示该数字符合规则
{
k++;
if(k==1)
t=n;
}

}
if(k==1)//表示只有一个数字符合
{
shudu[i][j]=t;
}
}
}
}
}

//回溯法得到数独题目的一个解。注:从第i,j个格子开始
void force(int i,int j)
{
int m;
int mm;//记录m是否符合规则
if(isok==1)//如果之前的回溯已经到达底部(即已得到一个解),则直接返回
return ;
if( shudu[i][j]==0)//如果未填
{
for(m=1;m<=9;m++)
{
mm=1;
mm=check(m,i,j);
if(mm)//如果m可以填入
{
shudu[i][j]=m;
if(j==8? ((i+1)%9==p&&q==0) : (i==p&&j+1==q) )//用来判断是否到了最后一个格子
{
isok=1;
write(shudu,shudutemp);//把此时的shudu存入shudutemp,避免了挖洞检验是否有解时会打印出来
return ;
}
if(j==8)
{
force((i+1)%9,0);
}
else
{
force(i,j+1);
}
}
}
shudu[i][j]=0;
}
else//如果已填直接看下一个格子
{
if(j==8? ((i+1)%9==p&&q==0) : (i==p&&j+1==q) )
{
isok=1;
write(shudu,shudutemp);
return ;
}
if(j==8)
{
force((i+1)%9,0);
}
else
{
force(i,j+1);
}
}
}
//数独计算器
int calculate(i,j)
{
p=i;
q=j;
isok=0;//确保每次计算前,isok为0
single();//先用唯一法解数独
force(i,j);//最后用回溯法求得一个解
return isok;
}
//数独生成器
void create()
{
int m ;
srand(time(0));
p=rand()%9;
q=rand()%9;
do{
init();
lasvgas();
printf("/n /n" );
m=calculate(p,q);
write(shudutemp,shudu);
}while(m==0);
}
//判断去掉shudu[m][n]的值后,是不是唯一解
int weiyi(int x,int m,int n)
{
int i,p=0,p1=0;
shudu[m][n]=0;

for(i=1;i<=9;i++)
{
if(i==x)
continue;
p=check(i,m,n);//判断此处是否可以填其他数字
if(p==1)//如果可以看填入其他数字后是否存在解
{
shudu[m][n]=i;
write(shudu,shudutemp1);
p1=calculate( 0, 0);
write(shudutemp1,shudu);
if(p1==1)//存在解的话,返回0
{
shudu[m][n]=x;//前面把shudu[m][n]的值变了,所以改回来
return 0;
}
}
}
shudu[m][n]=x;//前面把shudu[m][n]的值变了,所以改回来
return 1;
}
//根据用户所需难度挖洞
void dig()
{
int m,n, j,total=0,p=0;
printf("输入难度等级(1,2,3):/n");
scanf("%d",&j);
if(j==1)
{
while(total<25)//随机挖25个洞
{
srand(time(0));
//随机选取一个未填入数字的格子
do{
m=rand()%9;
n=rand()%9;
}while(shudu[m][n]==0);
p=weiyi(shudu[m][n],m,n);//判断可否挖去
if(p==1)
{
total++;
shudu[m][n]=0;
}
}
}
if(j==2)
{
for(m=0;m<9;m+=2)
{
for(n=0;n<9;n+=2)
{
p=weiyi(shudu[m][n],m,n);
if(p==1)//表示挖去后解唯一
shudu[m][n]=0;
}
}
for(m=1;m<9;m+=2)
for(n=1;n<9;n+=2)
{
p=weiyi(shudu[m][n],m,n);
if(p==1)//表示挖去后解唯一
shudu[m][n]=0;
}
}
if(j==3)
{
for(m=0;m<9;m++)
{
for(n=0;n<9;n++)
{
p=weiyi(shudu[m][n],m,n);
if(p==1)//表示挖去后解唯一
{
shudu[m][n]=0;
p=0;
}
}
}

}
}
//主函数
int single1()
{
int i,j,n,k=0,p=0,t;
for(i=0;i<9;i++)
{
for(j=0;j<9;j++)
{
k=0;
if(shudu[i][j]==0)
{
for(n=1;n<=9;n++)
{
p=check(n,i,j);
if(p==1)//表示该数字符合规则
{
k++;
if(k==1)
t=n;
}

}
if(k==1)//表示只有一个数字符合
{
a[i*9+j]=t;
return i*9+j;
}
}
}
}
return -1;
}
int tishi()
{
int m;
m=single1();
if(m==-1)
{
printf("已填满!");
return 1;
}
printf("第%d行第%d列只能填%d。按任意键继续....../n",m/9+1,m%9+1,a[m]);
shudu[m/9][m%9]=a[m];
getch();
print();
return 0;
}
int main()
{
int i,m,n;
char c,d;
create();//创建数独棋盘
for(m=0;m<9;m++)
for(n=0;n<9;n++)
shudutemp2[m][n]=shudu[m][n];
dig();//挖洞
print();//将题目打印
printf("是否需要提示?(y/n)");
scanf("/n%c",&c);
while(c=='y'||c=='Y')
{
i=tishi();
if(i==1)
return 0;
printf("是否继续提示?");
scanf("/n%c",&c);
}
printf("是否需要答案:y or n /n");
scanf("/n%c",&d);
if(d=='y')
{
for(m=0;m<9;m++)
{
for(n=0;n<9;n++)
printf("%d ",shudutemp2[m][n]);
printf("/n");
}

}
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值