数独9盒(3x3)递归回溯C所有组合
我写了一个3x3(9盒)的数独游戏,我想让它递归并生成所有的组合,但我不知道我哪里出错了。。。 这是我的密码:数独9盒(3x3)递归回溯C所有组合,c,recursion,C,Recursion,我写了一个3x3(9盒)的数独游戏,我想让它递归并生成所有的组合,但我不知道我哪里出错了。。。 这是我的密码: #include <iostream> #include <stdio.h> #include <stdlib.h> #define dbg 0 using namespace std; int n,st[100][100]; void afisare() { for(int i=1;i<=3;i++) { for(
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#define dbg 0
using namespace std;
int n,st[100][100];
void afisare()
{
for(int i=1;i<=3;i++) {
for(int j=1;j<=3;j++)
printf("%2d",st[i][j]);
printf("\n");
}
printf("\n");
}
int valid(int k,int ii,int jj)
{
int i,j;
// if(k==1){
// if(dbg)printf("\nReturnez 1 deoarece k-ul este egal cu 1");
// return 1;
// }
for(i=1;i<=3;i++)
for(j=1;j<=3;j++)
if((i==ii) && (j==jj))
{
if(dbg)
printf("\nValorile nu indeplinesc criteriul, se incrementeaza j.");
}
else
{
if(dbg)
printf("\n i= %d j= %d",i,j);
if(dbg)
printf("\nSe verifica daca %d este egal cu casuta %d %d",k,i,j);
if(k==st[i][j]) {
if(dbg)printf("\nValorile sunt egale, returnez 0");
return 0;
}
}
if(dbg)
printf("\nValorile nu sunt egale, deci returnez 1");
return 1;
}
void back(int k)
{
int i,j;
if(dbg) printf("\nk=",k);
for(i=1;i<=3;i++) {
for(j=1;j<=3;j++)
{
if(dbg)
printf("\nVerifica daca casuta %d %d este egal cu 0",i,j);
if(st[i][j]==0) {
if(dbg) printf("\n Este egal cu 0");
if(dbg) printf("\n%d ia valoarea %d.",st[i][j],k);
st[i][j]=k;
if(dbg)
printf("\nSe verifica valabilitatea numarului %d",st[i][j]);
// while(valid(k,i,j)!=0)
if(valid(k,i,j)!=0) {
valid(++k,i,j);
//back(k+1);
}
else
do {
st[i][j]=k+1;
if(dbg)
printf("\nCasuta %d %d are noua valoare %d, veche valoare fiind %d.",i,j,k+1,k);
if(dbg)
("\nValoarea returnata este 0, merg cu urmatoarea valoare %d si verific valabilitatea.",k+1);
//back(k+1);
}
while(valid(++k,i,j)==0);
}
else
if(dbg)
printf("\nNu este egala cu 0 ci are valoarea %d",st[i][j]);
}
}
if(k>9 || st[3][3]!=0)
afisare();
//afisare();
}
int main()
{
int i,j;
freopen("sudoku.in","r",stdin);
for(i=1;i<=3;i++)
for(j=1;j<=3;j++)
scanf("%d",&st[i][j]);
/* for(i=1;i<=3;i++){
for(j=1;j<=3;j++){
printf("%2d",st[i][j]);
}
printf("\n");
}*/
back(1);
system("pause");
return 0;
}
#包括
#包括
#包括
#定义dbg0
使用名称空间std;
int n,st[100][100];
void afisare()
{
对于(int i=1;i而言,代码的问题主要是格式化和命名。您可能已经设法将递归函数命名为back
,这就是为什么:
对于需要您找到给定起始条件的所有解决方案的问题,递归解决方案基本上是在解决方案空间中进行深度优先搜索。这意味着每个递归调用都是向前移动
您不需要通过调用一个方法来回溯,您可以通过不再调用forward方法来回溯,以允许DFS树(递归沿着其一条路径向下遍历)向上移动到它所查找的路径
在您的情况下,您正在使用一个全局数据结构(st
)来存储数独网格。这意味着,当您要回溯时,您需要将网格状态恢复到调用该方法之前的状态。在您的情况下,这意味着将您要签入的位置再次设置为0
这里有一个解决方案的指南,以及我解决它的方法(如果我使用全局数据结构,顺便说一下,这不是很好的编程实践。我建议使用类来存储网格)
首先,一些名称的变化:
将是我的解决方案中的void afisare()
(实现将是相同的)void printGrid()
将是int-valid(int,int,int)
,并且可以简化bool-isValid(int,int,int)
将是void back(int)
此函数将尝试用所有可能的数字填充下一个空格,对于所有有效的数字,它将向下递归并再次调用void fillEmpty()
。如果没有空数字,它将打印网格,因为您已找到解决方案fillEmpty()
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
int grid[3][3];
void
printGrid()
{
for(int i = 0; i < 3; ++i) {
for(int j = 0; j < 3; ++j)
printf("%d ", grid[i][j]);
printf("\n");
}
printf("\n");
}
bool
isValid(int k, int ii, int jj)
{
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j) {
if( (i != ii || j != jj) && grid[i][j] == k)
return false;
}
return true;
}
void
fillEmpty()
{
// Find the next empty position.
int i, j; bool foundEmpty = false;
for(i = 0; i < 3; ++i) {
for(j = 0; j < 3; ++j)
if(grid[i][j] == 0) {
foundEmpty = true;
break;
}
if(foundEmpty) break;
}
// If there are no empty positions left
// we have reached a solution, so we
// print it and do nothing else.
if(!foundEmpty)
{
printGrid();
return;
}
// Try every number
for(int k = 1; k <= 9; ++k)
if( isValid(k, i, j) )
{
grid[i][j] = k;
fillEmpty();
}
// Reset the grid position before backtracking.
grid[i][j] = 0;
}
int
main(void)
{
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
scanf("%d", &grid[i][j]);
printGrid();
fillEmpty();
return 0;
}
#包括
#包括
#包括
国际网格[3][3];
无效的
printGrid()
{
对于(int i=0;i<3;++i){
对于(int j=0;j<3;++j)
printf(“%d”,网格[i][j]);
printf(“\n”);
}
printf(“\n”);
}
布尔
isValid(int k,int ii,int jj)
{
对于(int i=0;i<3;++i)
对于(int j=0;j<3;++j){
if((i!=ii | | j!=jj)&&grid[i][j]=k)
返回false;
}
返回true;
}
无效的
fillEmpty()
{
//找到下一个空位置。
int i,j;bool foundEmpty=false;
对于(i=0;i<3;++i){
对于(j=0;j<3;++j)
如果(网格[i][j]==0){
foundEmpty=true;
打破
}
如果(发现为空)中断;
}
//如果没有剩余的空位置
//我们已经找到了解决办法,所以我们
//打印出来,不做其他事情。
如果(!foundEmpty)
{
printGrid();
回来
}
//试试每个号码
对于(int k=1;k)这是一段很长的不可读代码,没有任何英文注释,您能详细说明“出错”的原因吗?您有编译错误吗?您得到了什么?是的,我正在检查当前填充是否有效,如果有效,我将查找下一个值并进行检查,但我想在使用递归显示解决方案并执行所有组合后继续。@AlexandruBarbarosie 9*9*9是729(~2^10)…这不是一个很有帮助的评论。OP试图询问回溯的原理,而不是算法的效率。事实上,如果您自己实际实现并运行了解决方案,您将看到最坏的情况(完全空的网格)在几秒钟内完成,大部分时间用于IO。我尝试了您的代码并根据需要进行了更新,但是此代码不适用于9x9,我将3x3更改为9x9,但它不起作用,我知道它应该为每个数字和复选框添加一个/2 if's for row/collumn检查,但我在这里添加了调试行:for(k=1;k@asQuirreL我的计算又错了,我很匆忙,但你的也不正确。填充一个空的贪婪可能很快,不是因为算法好,而是因为它不是最坏的情况,相反,它是最好的!在最坏的情况下,当你感到数独贪婪时,你的算法会尝试在每个框中放9个数字(在他看来,所有9个都是有效的),其中8个没有通过进一步的测试(你无法感觉到更多的盒子)。这意味着每个盒子分支9次,这意味着9^81。