Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
数独9盒(3x3)递归回溯C所有组合_C_Recursion - Fatal编程技术网

数独9盒(3x3)递归回溯C所有组合

数独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(

我写了一个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(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。