【问题标题】:Sudoku 9 boxes ( 3x3 ) Recursion Backtracking in C all combinations数独 9 盒(3x3)C 所有组合中的递归回溯
【发布时间】:2014-01-07 11:28:26
【问题描述】:

我为 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;
}

正如我所说我不知道​​如何生成所有组合,我只认为我应该调用函数回溯而不是有效的......

【问题讨论】:

  • 这是一段很长的不可读的代码,没有任何英文的 cmets,你能详细说明一下“出了什么问题”吗?你有编译错误吗?你得到了什么?
  • 是的,我正在检查当前的填充是否有效,如果有效,我会选择下一个值并检查,但我想在递归显示解决方案后继续并进行所有组合。
  • @AlexandruBarbarosie 9*9*9 是 729 (~ 2^10)... 这不是一个很有帮助的评论。 OP试图询问回溯的原理,而不是算法的效率。事实上,如果您自己实际实现并运行了该解决方案,您会看到最坏的情况(一个完全空的网格)在几秒钟内完成,大部分时间都用于 IO。
  • 我尝试了您的代码并更新了我的必需品,但是此代码不适用于 9x9,我将 3x3 更改为 9x9 并且它不起作用,我知道它应该多 1/2 if's for row/column check for each number and box check,但我在这里添加了一个调试行:for(k=1;k
  • @asQuirreL 我的计算又错了,我很着急,但你的也不正确。填补一个空的贪婪可能很快不是因为算法好,而是因为它不是最坏的情况,相反,它是最好的!在最坏的情况下,当您感到数独贪婪时,您的算法会尝试在每个框中输入 9 个数字(所有 9 个在他的点上都是有效的),其中 8 个未通过进一步的测试(您无法进一步感觉到某个框)。这意味着每个盒子分支 9 次,这意味着 9^81。

标签: c recursion


【解决方案1】:

您的代码的问题主要是格式化和命名。您可能通过将递归函数命名为 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-24
    • 2017-12-16
    • 2012-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多