回溯经典n皇后的时间复杂度分析

Posted

技术标签:

【中文标题】回溯经典n皇后的时间复杂度分析【英文标题】:Time complexity analysis of backtracking classic n-queens 【发布时间】:2015-11-18 13:12:06 【问题描述】:

我一直在阅读一些解决经典 n-queens 问题的代码。只是找到一种解决方案(不是全部解决方案或计算解决方案的数量)。您可以在geeksforgeeks website 上找到完整的代码,总结如下。 问题是,这段代码的时间复杂度是多少,真的

bool isSafe(int board[N][N], int row, int col)

    int i, j;

    /* Check this row on left side */
    for (i = 0; i < col; i++)
        if (board[row][i])
            return false;

    /* Check upper diagonal on left side */
    for (i=row, j=col; i>=0 && j>=0; i--, j--)
        if (board[i][j])
            return false;

    /* Check lower diagonal on left side */
    for (i=row, j=col; j>=0 && i<N; i++, j--)
        if (board[i][j])
            return false;

    return true;


/* A recursive utility function to solve N
   Queen problem */
bool solveNQUtil(int board[N][N], int col)

    /* base case: If all queens are placed
      then return true */
    if (col >= N)
        return true;

    /* Consider this column and try placing
       this queen in all rows one by one */
    for (int i = 0; i < N; i++)
    
        /* Check if queen can be placed on
          board[i][col] */
        if ( isSafe(board, i, col) )
        
            /* Place this queen in board[i][col] */
            board[i][col] = 1;

            /* recur to place rest of the queens */
            if ( solveNQUtil(board, col + 1) )
                return true;

            /* If placing queen in board[i][col]
               doesn't lead to a solution, then
               remove queen from board[i][col] */
            board[i][col] = 0; // BACKTRACK
        
    

     /* If queen can not be place in any row in
        this colum col  then return false */
    return false;


/* This function solves the N Queen problem using
   Backtracking. It mainly uses solveNQUtil() to
   solve the problem. It returns false if queens
   cannot be placed, otherwise return true and
   prints placement of queens in the form of 1s.
   Please note that there may be more than one
   solutions, this function prints one  of the
   feasible solutions.*/
bool solveNQ()

    int board[N][N] =  0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0
    ;

    if ( solveNQUtil(board, 0) == false )
    
      printf("Solution does not exist");
      return false;
    

    printSolution(board);
    return true;

如果你回顾一下 cmets 的历史,有人会说它是 O(n!),甚至是指数级的。但我认为它们都不正确。

例如,对于O(n!) 的声明,一个给T(n)= n*(T(n-1) + O(n)) 导致O(n!)

为什么我认为这是错误的(不是O(n!))?

1.问题是,solveNQUtil 中的for 循环总是运行N 次。它不会随着问题范围n 而减少。所以上式中的乘数n是不正确的。应该换成固定号码N

2.isSafe 中的col 向下递推递归树,这意味着isSafe 中的for 循环的迭代次数越来越多。迭代次数为N - n

所以基本上,递归应该是T(n)= N *(T(n-1) + O(N - n))(N 是固定的)。不知道如何解决这个问题,但至少应该是O(N^N)有什么想法吗?

递归树法

如果使用递归树,则有O(n^n) 不同的路径向下到达叶子,并且每个路径都采用O(1+2+..n) 操作进行冲突检查。所以总时间应该是O(n^(n+2))是这样吗?

谁能指出这是否正确并给出适当的推理?

【问题讨论】:

【参考方案1】:

这是一个很好的观察。让我多多思考和模拟步骤。

通过仅查看代码并尝试推导复杂性,您实际上是正确的,它应该是 O(n^n)。

但问题是,尽管solveNQUtil 中的内部循环运行了 N 次,但递归函数 solveNQUtil 并没有被调用 N 次。在最坏的情况下,它将被称为n-1 次。因为前面的每一列都有一个皇后,所以当列被迭代时,其中一行被从进一步考虑中删除。所以不是重复发生

T(n)= N *(T(n-1))

确实是间接的

T(n)= n *(T(n-1))

【讨论】:

谢谢。确实如此。当再进行一次递归调用时,至少再有一次isSafe 应该返回false。因此,for 的迭代次数在每次递归调用时至少减少 1。而对于isSafe 中的开销,由于col 行已经被填充,所以最多有min(col, N-col+1) 迭代for (i = 0; i &lt; col; i++)。所以复发真的是T(n)= n*(T(n-1) + O(n)) 嘿,我考虑了一下。重复应为T(n) = n*T(n-1) + N*n,这导致O((n+1)!) 而不是O(n!)。这是因为虽然递归调用的次数减少了,但solveNQUtil() 中的for 循环迭代次数保持不变(N)。你怎么看? 告诉我这个重复是如何以 O(n+1 !) 结束的。试着举一些简单的例子,让我知道你发现了什么。结果证明这是一个很好的复杂性分析练习:)。虽然你是对的,N*n 项也会加起来。

以上是关于回溯经典n皇后的时间复杂度分析的主要内容,如果未能解决你的问题,请参考以下文章

求教C语言回溯法写出八皇后问题的92种解

经典问题-N皇后(回溯)C++实现

搜索八皇后

递归回溯之八皇后问题详解

AcWing 843. n-皇后问题 DFS

五大基本算法——回溯法