八皇后 C++ 递归算法和循环嵌套算法 共得到 92 种题解

八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后,为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当n = 1或n ≥ 4时问题有解。

八皇后问题最早是由西洋棋棋手马克斯·贝瑟尔(Max Bezzel)于1848年提出。第一个解在1850年由弗朗兹·诺克(Franz Nauck)给出。并且将其推广为更一般的n皇后摆放问题。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。

在此之后,陆续有数学家对其进行研究,其中包括高斯和康托,1874年,S.冈德尔提出了一个通过行列式来求解的方法,这个方法后来又被J.W.L.格莱舍加以改进。

1972年,艾兹格·迪杰斯特拉用这个问题为例来说明他所谓结构化编程的能力。他对深度优先搜索回溯算法有着非常详尽的描述。

八个皇后在8×8棋盘上共有4,426,165,368 种摆放方法,但只有92个互不相同的解。如果将旋转和对称的解归为一种的话,实际上一共只有12个独立解,

以上信息摘自维基百科 https://zh.wikipedia.org/zh-hans/%E5%85%AB%E7%9A%87%E5%90%8E%E9%97%AE%E9%A2%98

闲来无事,写了一段八皇后解题方法,共输出 92 种解题方案。

两种算法输出:分别是 eight.cpp 调用的 GenerateLoop() 循环嵌套算法,以及后的递归算法 GenerateRecursion(),两种算法本质相同,都是逐层搜索、回退、再搜索。而递归算法因为不受循环嵌套的限制,通过修改 main.h 中的“纵向数量”和“横向数量”的数值,可以实现 n×n 的计算,例如 10×10 的计算将找到 724 种解,以此类推。

eight.cpp,入口程序,创建八皇后对象 (封装在 main.h 和 main.cpp),EightQueen 对象有两个主要的生成函数,循环嵌套搜索算法 GenerateLoop() 和递归算法 GenerateRecursion(),它们分别执行生成过程和打印结果。

// eight.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "main.h"

using namespace std;

int main()
{
  // 创建八皇后对象
  EightQueen* eightQueen = new EightQueen();

  // 生成题解, 逐层搜索搜索, 层级受限
  eightQueen->InitCheckerboard();
  eightQueen->GenerateLoop();
  getchar();

  // 生成题解, 递归搜索算法
  eightQueen->InitCheckerboard();
  eightQueen->GenerateRecursion();
  getchar();

  return 0;
}

main.h,对象定义,封装所需的函数和内部变量。

#pragma once

#ifndef EIGHT_MAIN
#define EIGHT_MAIN

// 纵向数量, 递归状态下可以随意扩大
#define CHECKER_BOARD_ROW 8
// 横向数量, 递归状态下可以随意扩大
#define CHECKER_BOARD_COLUMN 8

class EightQueen
{
private:

  // 定义棋盘结构
  int checkerboard[CHECKER_BOARD_ROW][CHECKER_BOARD_COLUMN];

  // 初始化当前行
  void ClearRow(int row);
  // 检查向上冲突
  bool CheckVerticalByUp(int row, int column);
  // 检查左上角冲突
  bool CheckUpperLeftCorner(int row, int column);
  // 检查右上角冲突
  bool CheckUpperRightCorner(int row, int column);
  // 打印八皇后棋盘
  void TablePrint();
  // 计数
  int intPrintCount = 0;

public:
  // 初始化棋盘
  void InitCheckerboard();
  // 生成八皇后题解, 方法 1 嵌套循环
  void GenerateLoop();
  // 生成八皇后题解, 方法 2 递归
  void GenerateRecursion(int row = 0);

  EightQueen();
  ~EightQueen();
};

#endif    

main.cpp,主要代码的对象封装。

/*
 * 2018-8-13
 *
 * 创建一个 8x8 棋盘, 0 表示没有皇后,1表示存在皇后
 *
 * 循环嵌套算法实现
 * EightQueen::GenerateLoop()
 *
 * 递归算法实现
 * EightQueen::GenerateRecursion(int row)
 *
 * loop begin
 * 逐层循环,第一层不做检查
 * 每一层的每一列进行检查
 * 取坐标,检查向上是否存在冲突,如果有则返回 false,否则返回 true
 * 取坐标,检查左上角方向是否存在冲突,如果有则返回 false,否则返回 true
 * 取坐标,检查右上角方向是否存在冲突,如果有则返回 false,否则返回 true
 * 将皇后设置到某个指定的坐标
 * loop end
 *
 */

#include "stdafx.h"
#include <stdio.h>
#include "main.h"

 // using namespace std;

EightQueen::EightQueen()
{
}

EightQueen::~EightQueen()
{
}

// 初始化棋盘
void EightQueen::InitCheckerboard()
{
  for (int i = 0; i < CHECKER_BOARD_ROW; i++)
  {
    for (int j = 0; j < CHECKER_BOARD_COLUMN; j++)
    {
      checkerboard[i][j] = 0;
    }
  }
  // 计数器
  intPrintCount = 0;
}

// 打印八皇后解题表格
void EightQueen::TablePrint()
{
  // 逐行输出棋盘信息
  for (int row = 0; row < CHECKER_BOARD_ROW; row++)
  {
    // 设置表宽度
    const int PrintLineLen = CHECKER_BOARD_COLUMN * 2;
    // 定义输出表的行, +1 需要一个字符串结束符 0x0
    char strPrintLine[PrintLineLen + 1];
    // 初始化输出表的行
    memset(strPrintLine, 0x0, PrintLineLen + 1);

    // 绘制棋盘行, 用 " " 和 "." 标记
    for (int i = 0; i < PrintLineLen; i++)
    {
      // 创建表格, 留一空格跳空,更易于识别
      if (i % 2 == 0)
      {
        strPrintLine[i] = '.';
      }
      else
      {
        strPrintLine[i] = ' ';
      }
    }

    // 遍历棋盘的行, 找到皇后, 则标记为 'x'
    for (int i = 0; i < CHECKER_BOARD_COLUMN; i++)
    {
      // 找到一个皇后,标记为 x
      if (checkerboard[row][i] == 1)
      {
        // 跳空置子
        strPrintLine[i * 2] = 'x';
      }
    }
    // 打印一行
    printf("%s\n", strPrintLine);
  }
}

// 检查垂直向上的冲突
bool EightQueen::CheckVerticalByUp(int row, int column)
{
  // 避开当前的皇后位置
  row--;
  // 保持列,检查所有行是否存在皇后
  while (row >= 0)
  {
    int val = checkerboard[row--][column];
    if (val == 1)
    {
      return false;
    }
  }

  return true;
}

// 检查左上对角线的冲突
bool EightQueen::CheckUpperLeftCorner(int row, int column)
{
  // 避开当前的皇后
  row--;
  column--;

  // 检查左上角所有的位置是否有皇后
  while (row >= 0 && column >= 0)
  {
    int val = checkerboard[row--][column--];
    if (val == 1)
    {
      return false;
    }
  }

  return true;
}

// 检查右上对角线的冲突
bool EightQueen::CheckUpperRightCorner(int row, int column)
{
  // 避开当前的皇后
  row--;
  column++;

  // 检查左上角所有的位置是否有皇后
  while (row >= 0 && column < CHECKER_BOARD_COLUMN)
  {
    int val = checkerboard[row--][column++];
    if (val == 1)
    {
      return false;
    }
  }

  return true;
}

// 初始化当前行
void EightQueen::ClearRow(int row)
{
  for (int i = 0; i < CHECKER_BOARD_COLUMN; i++)
  {
    checkerboard[row][i] = 0;
  }
}

// 逐个生成八皇后位置 递归算法
void EightQueen::GenerateRecursion(int row)
{
  // 找到结果 显示结果并返回
  if (row == CHECKER_BOARD_ROW)
  {
    // row == CHECKER_BOARD_ROW 已经完成了棋盘上全部 row 的布子
    // 打印棋盘
    TablePrint();
    printf("No.%d\n", ++intPrintCount);
    return;
  }

  // 在当前 row 内找到一个适合的位置放置皇后
  for (int c = 0; c < CHECKER_BOARD_COLUMN; c++)
  {
    // 第一行不作检查
    if (row == 0)
    {
    }
    else
    {
      // 向上检查垂直冲突
      if (CheckVerticalByUp(row, c) == false)
      {
        continue;
      }

      // 检查左上角冲突
      if (CheckUpperLeftCorner(row, c) == false)
      {
        continue;
      }

      // 检查右上角冲突
      if (CheckUpperRightCorner(row, c) == false)
      {
        continue;
      }
    }

    // 当前 row 找到一个适合的位置
    // 清空当前行
    ClearRow(row);
    // 置子
    checkerboard[row][c] = 1;
    // 递归进入下一行
    GenerateRecursion(row + 1);

    // 若循环内全部被 continue,将回退到上一 row 重新分配位置,直到全部棋盘可能性搜索完
  }
}

// 逐行生成八皇后位置,循环嵌套算法
void EightQueen::GenerateLoop()
{
  // 计数器
  int count = 0;

  // 第1行
  for (int c0 = 0; c0 < CHECKER_BOARD_ROW; c0++)
  {
    int row = 0;
    ClearRow(row);
    checkerboard[row][c0] = 1;

    // 第2行
    for (int c1 = 0; c1 < CHECKER_BOARD_ROW; c1++)
    {
      int row = 1;

      // 向上检查垂直冲突
      if (CheckVerticalByUp(row, c1) == false)
      {
        continue;
      }

      // 检查左上角冲突
      if (CheckUpperLeftCorner(row, c1) == false)
      {
        continue;
      }

      // 检查右上角冲突
      if (CheckUpperRightCorner(row, c1) == false)
      {
        continue;
      }

      // 放置皇后
      ClearRow(row);
      checkerboard[row][c1] = 1;

      for (int c2 = 0; c2 < CHECKER_BOARD_ROW; c2++)
      {
        int row = 2;

        // 向上检查垂直冲突
        if (CheckVerticalByUp(row, c2) == false)
        {
          continue;
        }

        // 检查左上角冲突
        if (CheckUpperLeftCorner(row, c2) == false)
        {
          continue;
        }

        // 检查右上角冲突
        if (CheckUpperRightCorner(row, c2) == false)
        {
          continue;
        }

        ClearRow(row);
        checkerboard[row][c2] = 1;

        for (int c3 = 0; c3 < CHECKER_BOARD_ROW; c3++)
        {
          int row = 3;

          // 向上检查垂直冲突
          if (CheckVerticalByUp(row, c3) == false)
          {
            continue;
          }

          // 检查左上角冲突
          if (CheckUpperLeftCorner(row, c3) == false)
          {
            continue;
          }

          // 检查右上角冲突
          if (CheckUpperRightCorner(row, c3) == false)
          {
            continue;
          }

          ClearRow(row);
          checkerboard[row][c3] = 1;

          for (int c4 = 0; c4 < CHECKER_BOARD_ROW; c4++)
          {
            int row = 4;

            // 向上检查垂直冲突
            if (CheckVerticalByUp(row, c4) == false)
            {
              continue;
            }

            // 检查左上角冲突
            if (CheckUpperLeftCorner(row, c4) == false)
            {
              continue;
            }

            // 检查右上角冲突
            if (CheckUpperRightCorner(row, c4) == false)
            {
              continue;
            }

            ClearRow(row);
            checkerboard[row][c4] = 1;

            for (int c5 = 0; c5 < CHECKER_BOARD_ROW; c5++)
            {
              int row = 5;

              // 向上检查垂直冲突
              if (CheckVerticalByUp(row, c5) == false)
              {
                continue;
              }

              // 检查左上角冲突
              if (CheckUpperLeftCorner(row, c5) == false)
              {
                continue;
              }

              // 检查右上角冲突
              if (CheckUpperRightCorner(row, c5) == false)
              {
                continue;
              }

              ClearRow(row);
              checkerboard[row][c5] = 1;

              for (int c6 = 0; c6 < CHECKER_BOARD_ROW; c6++)
              {
                int row = 6;

                // 向上检查垂直冲突
                if (CheckVerticalByUp(row, c6) == false)
                {
                  continue;
                }

                // 检查左上角冲突
                if (CheckUpperLeftCorner(row, c6) == false)
                {
                  continue;
                }

                // 检查右上角冲突
                if (CheckUpperRightCorner(row, c6) == false)
                {
                  continue;
                }

                ClearRow(row);
                checkerboard[row][c6] = 1;

                for (int c7 = 0; c7 < CHECKER_BOARD_ROW; c7++)
                {
                  int row = 7;

                  // 向上检查垂直冲突
                  if (CheckVerticalByUp(row, c7) == false)
                  {
                    continue;
                  }

                  // 检查左上角冲突
                  if (CheckUpperLeftCorner(row, c7) == false)
                  {
                    continue;
                  }

                  // 检查右上角冲突
                  if (CheckUpperRightCorner(row, c7) == false)
                  {
                    continue;
                  }

                  // 找到位置并置子
                  ClearRow(row);
                  checkerboard[row][c7] = 1;
                  // 打印表格
                  printf("No.%d: R0=%d, R1=%d, R2=%d, R3=%d, R4=%d, R5=%d, R6=%d, R7=%d\n", ++count, c0, c1, c2, c3, c4, c5, c6, c7);
                  TablePrint();
                }
              }
            }
          }
        }
      }
    }
  }

  printf("Found %d, press enter to exist.", count);
}

八皇后问题的解输出,两种算法都各找到 92 种解。

《八皇后 C++ 递归算法和循环嵌套算法 共得到 92 种题解》

 

Q群讨论 236201801
.

    原文作者:八皇后问题
    原文地址: https://blog.csdn.net/joyous/article/details/81714062
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞