n-Queen 算法的所有可能解

Posted

技术标签:

【中文标题】n-Queen 算法的所有可能解【英文标题】:All possible solution of the n-Queen's algorithm 【发布时间】:2011-10-11 17:59:13 【问题描述】:

在为 n-Queen 问题的所有可能解决方案实施算法时,我发现许多分支都达到了相同的解决方案。有没有什么好方法可以为 n-Queens 问题生成每个独特的解决方案?如何避免不同分支产生的重复解(store和compare除外)?

这是我尝试过的第一个解决方案: http://www.ideone.com/hDpr3

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* crude */

#define QUEEN 'Q'
#define BLANK '.'

int is_valid (char **board, int n, int a, int b)

  int i, j;

  for (i=0; i<n; i++)
  
    if (board[a][i] == QUEEN)
      return 0;
    if (board[i][b] == QUEEN)
      return 0;
  

  for (i=a, j=b; (i>=0) && (j>=0); i--, j--)
  
    if (board[i][j] == QUEEN)
      return 0;
  

  for (i=a, j=b; (i<n) && (j<n); i++, j++)
  
    if (board[i][j] == QUEEN)
      return 0;
  

  for (i=a, j=b; (i>=0) && (j<n); i--, j++)
  
    if (board[i][j] == QUEEN)
      return 0;
  

  for (i=a, j=b; (i<n) && (j>=0); i++, j--)
  
    if (board[i][j] == QUEEN)
      return 0;
  
  return 1;


void show_board (char **board, int n)

  int i, j;

  for (i=0; i<n; i++)
  
    printf ("\n");
    for (j=0; j<n; j++)
    
      printf (" %c", board[i][j]);
    
  


int nqdfs_all (char **board, int n, int d)

  int i, j, ret = 0;

  /* the last queen was placed on the last depth
   * therefore this dfs branch in the recursion 
   * tree is a solution, return 1
   */
  if (d == n)
  
    /* Print whenever we find one solution */
    printf ("\n");
    show_board (board, n);
    return 1;
  

  /* check all position */
  for (i=0; i<n; i++)
  
    for (j=0; j<n; j++)
    
      if (is_valid (board, n, i, j))
      
    board[i][j] = QUEEN;
    nqdfs_all (board, n, d + 1);
    board[i][j] = BLANK;
      
    
  

  return ret;  


int nqdfs_first (char **board, int n, int d)

  int i, j, ret = 0;

  /* the last queen was placed on the last depth
   * therefore this dfs branch in the recursion 
   * tree is a solution, return 1
   */
  if (d == n)
    return 1;

  /* check all position */
  for (i=0; i<n; i++)
  
    for (j=0; j<n; j++)
    
      if (is_valid (board, n, i, j))
      
    board[i][j] = QUEEN;
    ret = nqdfs_first (board, n, d + 1);
    if (ret)
    
      /* if the first branch is found, tell about its 
       * success to its parent, we will not look in other
       * solutions in this function.
       */
      return ret;
    
    else
    
      /* this was not a successful path, so replace the
       * queen with a blank, and prepare board for next
       * pass
       */
      board[i][j] = BLANK;
    
      
    
  

  return ret;


int main (void)

  char **board;
  int n, i, j, ret;

  printf ("\nEnter \"n\": ");
  scanf ("%d", &n);

  board = malloc (sizeof (char *) * n);
  for (i=0; i<n; i++)
  
    board[i] = malloc (sizeof (char) * n);
    memset (board[i], BLANK, n * sizeof (char));
  

  nqdfs_first (board, n, 0);
  show_board (board, n);

  printf ("\n");
  return 0;

为了生成所有可能的解决方案,我使用了相同的代码nqdfs_all () 函数,但没有将控件返回给父级,而是继续枚举和检查。调用此函数会显示不同分支达到的重复结果。

【问题讨论】:

每个解决方案都有等效的排列和旋转。例如,您可以旋转每个解决方案并存储/比较现有集合。 (并没有真正减少您的计算时间 - 这是我假设之后的结果...... 【参考方案1】:

您应该利用每个皇后必须放置在不同列中的事实。如果您已经在前 k 列中放置了 k 个皇后,则递归地将皇后编号 k+1 放置在第 k+1 列中并遍历第 1 到第 n 行(而不是像您当前的算法那样遍历所有 n*n 单元格)。对于每个有效的展示位置,继续使用 k:=k+1。这将避免任何重复的结果,因为此算法根本不会生成任何重复的板。

编辑:关于避免对称性的问题:可以预先避免其中的一部分,例如,通过将第 1 列中的皇后 1 限制为第 1 行,...n/2。如果要完全避免输出对称解决方案,则必须将找到的每个解决方案存储在列表中,并且每当找到新解决方案时,在将其打印出来之前,测试其中一个对称等价物是否已经存在于列表中。

为了提高效率,您可以使用每个板的“规范表示”,定义如下。生成给定一个的所有对称板,将每个板打包成一个字节数组,并在这些数组中保留数组,该数组被解释为一个大数,具有最小值。这种打包表示是每个板的对称类的唯一标识符,可以很容易地放入字典/哈希表中,这使得测试该对称类是否已经出现非常有效。

【讨论】:

虽然,它不会删除对称等价物(由于旋转或反射)。 @comingstorm:是的,但这不是 OP 的问题。 虽然不是问题,但是如何识别反射、旋转类似的解决方案? @DocBrown 感谢您的编辑。我想了一个类似的方法,完全避免对称解决方案看起来有点困难,最好尝试用具有相同值的对称相似板来散列一个板。

以上是关于n-Queen 算法的所有可能解的主要内容,如果未能解决你的问题,请参考以下文章

算法:N皇后问题51. N-Queens

算法:N皇后问题51. N-Queens

N-Queens 谜题,但包含所有棋子

基于象鼻虫损害优化算法求解n-Queen问题(Matlab代码实现)

遗传算法优化

贪心算法总结