数独递归回溯,过早取消递归

4
我正在用C++编写数独求解器,在编写过程中遇到了一些问题。下面是我的解决方案代码。它可以解决数独谜题的前三行,但在第四行结束时就会出现递归失效。使用gdb调试代码后,发现程序已经到达了第四行的结尾,然后回溯到第六列,进行尝试,但最终还是递归失败退出了。
代码中有几个注意事项,存储数独板块的矩阵从(1, 1)处开始,而不是从(0, 0)处开始。因此,当首次调用solveBoard时,参数为(1, 1, 0)。我还附加了setCell和checkConflicts函数,以便更好地了解代码情况。我有三个向量rowConf、colConf和squConf,用于存储已放置在相应行、列或九宫格中的值。我已经呆了几个小时,无法让程序通过第三行。非常感谢您提供帮助!谢谢!
编辑:添加了clearCell()
bool board::solveBoard(int i, int j, int count)
{

    if (j > 9)
    {
        j = 1;
        i++;

        printBoard();
        if (isSolved())
        {
            printBoard();
            cout <<"The Board has been solved!" <<endl
                 <<" The number of recursive calls was: " <<count <<endl;
            return true;
        }
     }

     if (isBlank(i, j))
     {
         for (int n = 1; n < 10; n++)
         {
             if (setCell(i, j, (char)n + '0'))
             {
                 if (solveBoard(i, j + 1, count + 1))
                 {
                     return true;
                 }
              }
          }
      }
      else
      {
          return (solveBoard(i, j + 1, count + 1));
      }

      clearCell(i, j);
      return false;
}

bool board::setCell(int i, int j, char val)
{
    int intVal;

    intVal = atoi(&val);

    if (i >= 1 && i <= BoardSize && j >= 1 && j <= BoardSize &&
        intVal >= 1 && intVal <= BoardSize)
    {
        if (!(checkConflicts(intVal, i, j, squareNumber(i, j))))
        {
        return false;
        }

        value[i][j] = intVal;

        // Set flags of the conflicts
        rowConf[i][intVal] = true;
        colConf[j][intVal] = true;
        squConf[squareNumber(i, j)][intVal] = true;

        return true;
    }
    else
    {
        throw rangeError("bad value in setCell");
    }
}

bool board::checkConflicts(int val, int i, int j, int k)
{
    if (i < 1 && i > BoardSize && j < 1 && j > BoardSize &&
        k < 1 && k > BoardSize && val < 1 && val > BoardSize)
    {
        throw rangeError("bad value in checkConflicts()");
    }

    if (rowConf[i][val] || colConf[j][val] || squConf[k][val])
    {
        return false;
    }
    else
    {
        return true;
    }
}

Initial Board:
 -----------------------------
| 3       |    8    |          -----------------------------
|         | 7       |       5  -----------------------------
| 1       |         |          ----------------------------- 
 -----------------------------
|         |         | 3  6     -----------------------------
|       2 |       4 |          -----------------------------
|    7    |         |          -----------------------------
 -----------------------------
|         |    6    | 1  3     -----------------------------
|    4  5 | 2       |          -----------------------------
|         |         | 8        -----------------------------
 -----------------------------

Final Output:
 -----------------------------
| 3  2  4 | 1  8  5 | 6  7  9  -----------------------------
| 6  8  9 | 7  2  3 | 4  1  5  -----------------------------
| 1  5  7 | 4  9  6 | 2  8  3  -----------------------------
 -----------------------------
|         |         | 3  6     -----------------------------
|       2 |       4 |          -----------------------------
|    7    |         |          -----------------------------
 -----------------------------
|         |    6    | 1  3     -----------------------------
|    4  5 | 2       |          -----------------------------
|         |         | 8        -----------------------------
 -----------------------------

void board::clearCell(int i, int j)
{
    int intVal;

    if (i >= 1 && i <= BoardSize && j >= 1 && j <= BoardSize)
    {
        if (value[i][j] != -1)
        {
            intVal = value[i][j];
            rowConf[i][intVal] = false;
            colConf[j][intVal] = false;
            squConf[squareNumber(i, j)][intVal] = false;
            value[i][j] = -1;
         }
    }
    else
    {
        throw rangeError("bad value in setCell");
    }
}

另外,请提供 clearCell 函数。 - phant0m
是的,最终它会回到当i=1且j=2的时候。当它尝试放置一个值以尝试新的路径时,该行的rowConf显示除了9之外所有的值都已经存在。因此,它放置了9,继续到1,3,但由于代码认为没有有效的选择,所以无法再放置更多的选择。出于某种原因,它没有将该行的rowConf重新设置为false。 - zberry92
你知道吗,你能把所有其他的函数也加上吗?这样比逐步请求更容易 ;) - phant0m
你解决了问题还是还需要帮助? - phant0m
我已经尝试将您的测试数据(初始棋盘)输入到我的求解器和其他求解器中,但它们都没有找到解决方案!也许您应该从一个有解决方案的棋盘开始进行测试 ;) - Jens Schwarzer
显示剩余3条评论
2个回答

0

你的问题很可能出在这里:

if (isBlank(i, j))
 {
     for (int n = 1; n < 10; n++)
     {
         if (setCell(i, j, (char)n + '0'))
         {
             if (solveBoard(i, j + 1, count + 1))
             {
                 return true;
             }
          }
      }
  }

不知何故它正在通过这个部分,这就是为什么最终没有进入else的原因,但由于它之前没有返回,所以它被卡住了。

这需要更多的调试,但这里有一个可能导致解决方案的想法:

if (isBlank(i, j))
{
    for (int n = 1; n < 10; n++)
    {
        if (setCell(i, j, (char)n + '0'))
        {
            if (solveBoard(i, j + 1, count + 1))
            {
                return true;
            } else {
                echo 'Looks like it ended on the farthest-level..';
          } 
      } else {
          echo 'Looks like it ended on the second-farthest level.';
      }
  }

为什么你有多个else子句?此外,你的else基本上是这样说的:“如果第一次没有成功,让我们再试一次”。那不会起作用。 - phant0m
@phant0m,谢谢,正在编辑。只有一个“if”没有运行。无论如何,我的观点是,看起来这就是 OP 代码退出的地方, OP 应该找到一种追踪的方法。 - Fernando Cordeiro

0

atoi函数期望一个字符串作为参数,即以字符'\0'(ASCII NUL)结尾的字符数组。你提供了一个指向字符的指针参数(相当于一些字符数组),但不能保证它是以零结尾的。请用intVal = (int)val - '0';替换intVal = atoi(&val);

而你的checkConflicts应该在第一个if中使用||运算符而不是&&

这些可能不是错误的原因,但肯定需要更正。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接