回溯法之八皇后问题简单理解

回溯法,简单理解就是有源可溯。基本思想要借鉴穷举法,但是它不是一味地穷举,当发现某一步不符合条件时,这一步后面的穷举操作就不进行了(俗称“剪枝”),我自己把它叫做动态穷举法。假设第一个步骤可行,那么执行第二个步骤,第三个......如果其中第三个步骤不行,那么我们再回过来(回溯),第二个步骤换一种方法尝试,然后再重新第三个步骤,第四个......直到完成任务要求为止。

这里,以八皇后问题为例。试图把回溯法讲清楚。

注意:递归应该是一种算法结构,回溯法是一种算法思想。

何为八皇后问题?

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

八皇后问题最早是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出。之后陆续有数学家对其进行研究,其中包括高斯和康托,并且将其推广为更一般的n皇后摆放问题。八皇后问题的第一个解是在1850年由弗朗兹·诺克给出的。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。1874年,S.冈德尔提出了一个通过行列式来求解的方法,这个方法后来又被J.W.L.格莱舍加以改进。

艾兹格·迪杰斯特拉在1972年用这个问题为例来说明他所谓结构性编程的能力。

八皇后问题出现在1990年代初期的著名电子游戏第七访客中。

解题思想

我们采用回溯法来解决这个问题。还记得我说的动态穷举法(“剪枝”)?那么我们就开始穷举吧。过程请看下图。八个皇后,每个皇后放一行,那么我们要确定的就是每行皇后要放的列的位置。对于第一行,假设把皇后放在第一列(这里就开始了一个for循环了)。第一步当然满足,然后我们看第二行(又开始一个for循环啦),假设把第二个皇后放在(2,1)(行,列)处,不行(“剪枝”)!那继续for,放在(2,2)处,不行(“剪枝”)!继续for,放在(2,3)处。Bingo!那我们进行第三步,第四步....也许,在第三步当中,执行完第三步的八次for循环后,仍未有合理的答案。那么就得返回第二步了。这时候,把第二个皇后放在(2,4)处。继续......

(参考http://blog.csdn.net/justme0/article/details/7540425)

         

接下来,我们就要尝试把这个过程转化成伪代码。

//寻找当前行的皇后应该位于哪一列
void FindQueen(row)
{
  for(int i=0;i<8;i++)
  {
    //1.判断当前是否满足要求
    if(Is_Meet(row,i))
    {
    //2.判断当前是否是最后一行了
      if(最后一行){输出操作,并返回}
    //3.执行下一行匹配
      FindQueen(row+1);
    //4.如果进行到这一步,说明步骤三已经操作完,没有合适结果,需要返回上一步,即执行当前for的下一个循环
    }
  }
}

判断是否满足条件

这里,有一步我们需要再考虑下的,即第1步,如何判断当前插入的皇后满足条件。

根据国际象棋的规则,不能有两个皇后位于:1)同一列;2)同一对角线。

1)同一列

比较好判断,列号相同,即属于同一列;

2)同一对角线

有两种位置:正斜对角线和反斜对角线。判断条件分别为:(r1+c1)==(r2+c2),(r1-c1)==(r2-c2)

将新的皇后位置依次与已经插入过的皇后位置进行比较判断即可。

具体实现

看到以上代码,应该有点思路了吧。上具体实现代码。

bool Is_Meet(int row,int column)
{
    int c;
    for(int r=0;r<row;r++)
    {
        c=queen[r];
        if(column==c)
            return 0;
        if((row+column)==(c+r))
            return 0;
        if((column-row)==(c-r))
            return 0;
    }
    return 1;
}

void findQueen(int row)
{
    for(int c=0;c<8;c++)
    {
        if(Is_Meet(row,c))
        {
            queen[row]=c;
            if(row==7)
            {
                print();
                return;
            }
            findQueen(row+1);
        }
        queen[row]=-1;
    }
}

为了好玩,简单地改了下,使其可以在控制台动态显示匹配过程。

#include "iostream"
#include "string"
#include <Windows.h>
using namespace std;
int queen[8];

void print()
{
    system("cls");
    cout << "八皇后问题动态演示\n";
    cout<< "------------------------\n";
    for (int outer = 0; outer < 8; outer++)
    {
        if(queen[outer]!=-1)
        {
        for (int inner = 0; inner < queen[outer]; inner++)
            cout << " . ";
        cout<<" # ";
        }
        for (int inner = queen[outer] + 1; inner < 8; inner++)
            cout << " . ";
        cout<<endl;
    }
}

bool Is_Meet(int row,int column)
{
    int c;
    for(int r=0;r<row;r++)
    {
        c=queen[r];
        if(column==c)
            return 0;
        if((row+column)==(c+r))
            return 0;
        if((column-row)==(c-r))
            return 0;
    }
    return 1;
}

void findQueen(int row)
{
    for(int c=0;c<8;c++)
    {
        Sleep(1000);
        print();
        if(Is_Meet(row,c))
        {
            queen[row]=c;
            if(row==7)
            {
                //print();
                return;
            }
            findQueen(row+1);
        }
        queen[row]=-1;
    }
}
int main()
{
    memset(queen,-1,8*sizeof(int));//这里是赋-1,故不会出错,要清楚memset是依次对单个字节进行赋值
    findQueen(0);
}
时间: 2024-10-14 05:53:20

回溯法之八皇后问题简单理解的相关文章

算法学步:回溯法-8皇后问题

问题描述: 有八个皇后(可以当成八个棋子),如何在 8*8 的棋盘中放置八个皇后,使得任意两个皇后都不在同一条横线.纵线或者斜线上 做法: 从第一行开始,一行一行地考虑,这样起码可以保证皇后不在同一行: 考虑下面的每一行的时候,需要让新增加的棋子不在前面添加的棋子的左下.正下.右下,即新增加的棋子的列不等于前面棋子的列,它的行号+列号不等于前面棋子的行号加列号(不在左下),|行号-列号|不等于之前棋子的|行号-列号|(不在右下): 如果该行所有位置都不符合要求,则回溯到前一行,改变皇后的位置,继

回溯算法之八皇后问题

1 package 回溯; 2 3 public class 八皇后递归回溯 { 4 5 private int length; //皇后数量 6 private int sum; //总方案数 7 private int num[]; //存放皇后的一维数组 8 9 public 八皇后递归回溯() { 10 length = 8; 11 sum=0; 12 num = new int[length + 1]; 13 } 14 15 public boolean check(int x) {

回溯法---n皇后问题(4)

body { font-family: 微软雅黑,"Microsoft YaHei", Georgia,Helvetica,Arial,sans-serif,宋体, PMingLiU,serif; font-size: 10.5pt; line-height: 1.5; } html, body { } h1 { font-size:1.5em; font-weight:bold; } h2 { font-size:1.4em; font-weight:bold; } h3 { fon

每天刷个算法题20160519:回溯法解八皇后

版权所有.所有权利保留. 欢迎转载,转载时请注明出处: http://blog.csdn.net/xiaofei_it/article/details/51502622 为了防止思维僵化,每天刷个算法题.已经刷了几天了,现在发点代码. 我已经建了一个开源项目,每天的题目都在里面: https://github.com/Xiaofei-it/Algorithms 绝大部分算法都是我自己写的,没有参考网上通用代码.读者可能会觉得有的代码晦涩难懂,因为那是我自己的理解. 最近几天都是在写一些原来的东西

回溯法求解八皇后问题---(初步、未优化)

首先介绍一下回溯算法: 定义来自<百度百科>......名字着很高大上,实际上就是试探法,逐步试错找到最终的可行解. 重要的一点是解空间通常是在搜索可行解过程中动态产生的,所以程序中通常利用到递归的算法,如后面介绍的八皇后问题.这点区别与于前段时间所写的模拟退火算法,模拟退火是首先确定解空间,然后以一定的概率接受当前发现的次优解,从而有更大的可能避免局部最优而得到全局最优. 简单介绍一下八皇后问题: 在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或

八皇后问题-回溯法(matlab)

1.问题描述 八皇后问题是十九世纪著名数学家高斯于1850年提出的.问题是:在8*8的棋盘上摆放8个皇后,使其不能互相攻击,即任意的两个皇后不能处在同意行,同一列,或同意斜线上. 2.matlab代码 function PlaceQueen(row,stack,N)%回溯法放置皇后 if row>N PrintQueen(N,stack);%打印棋盘 else for col=1:N stack(row)=col; if row==1||Conflict(row,col,N,stack)%检测是

回溯法编程技巧

1. 什么是回溯法 引用一下维基百科对回溯法的介绍: 回溯法(英语:backtracking)是暴力搜索法中的一种. 对于某些计算问题而言,回溯法是一种可以找出所有(或一部分)解的一般性算法,尤其适用于约束满足问题(在解决约束满足问题时,我们逐步构造更多的候选解,并且在确定某一部分候选解不可能补全成正确解之后放弃继续搜索这个部分候选解本身及其可以拓展出的子候选解,转而测试其他的部分候选解). 在经典的教科书中,八皇后问题展示了回溯法的用例.(八皇后问题是在标准国际象棋棋盘中寻找八个皇后的所有分布

算法回顾--N皇后问题简单回顾

前言 最近学习的过程中,不知道哪门子的思维发散,突然又遇见皇后问题了,于是乎老调重弹,心里琢磨,虽然思路大家都容易懂,哪怕是最简单的野蛮回溯法,说着简单,但是如果非得编码实现?我可以一次性写出来OK的代码吗?我对此表示疑问,于是乎动手写代码,发现写此类算法问题,最重要的是边界条件的判断.这里说明一下,这篇纯属练手,不考虑算法效率,只是为了实现,为了练习最暴力野蛮的回溯,说白了,就是怎样简单粗暴的玩弄for和while这些个玩意! 实现 本人比较懒,所以懒得搞二维数组来存储皇后坐标,其实用二维数组

AI -- 回溯法解决四皇后问题

AI -- 回溯法解决四皇后问题 回溯法 四皇后 C语言 问题描述 在 4*4 的棋盘上无冲突的摆放 4 个皇后,无冲突是指一个皇后所在位置的水平.竖直以及斜线上不能出现其他的皇后,其他的 n 皇后问题以此类推 解决方法 按行来摆放棋子,下一行的摆放满足于与上一行的棋子没有冲突,否则就返回上一步走其他的路线,这就是所谓的回溯法 详细说明 在第一行有四种可能,选择第一个位置放上皇后 第二行原本可以有四种可能摆放,但是第一第二个已经和第一行的皇后冲突了,因此只剩下第三第四个格子了,先选择第三个格子