八皇后(c++启发式函数求解)

八皇后问题是回溯算法的典型案例,在回溯法中,常常是盲目搜索,耗费过多的搜索时间。在本次实验中,使用了启发式搜索,搜索时不是任取一个分支,而是选择最佳的分支往下搜索。通过定义状态空间、操作规则、搜索策略,我们可以清晰快速地得到原问题的一个解。

八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。通过计算机编程,我们可以快速地求出问题的解。

状态空间

(i,C[i]), i = 0,1,…,7;
(i,C[i])表示第i行的皇后放置在第C[i]列。
初始状态为C[i] = -1, i = 0,1,…,7;表示所有行都不放置皇后。
目标状态为C[i] != -1, i = 0,1,…,7;表示所有行都已经放置了皇后。

操作规则

第一个皇后放在第一行;
第二个皇后放在第二行且不与第一个皇后在同一列或对角线的空格上;
……
第i个皇后放在第i行且不与前面i-1个皇后在同一列或对角线的空格上。

搜索策略

由于在某一步放置某个皇后时,可能有多个空格可以使用,所以定义启发式函数:

   fx = 剩下未放行中能够用来放皇后的空格数

如果第i行的皇后放在第j列合法,计算启发式函数的值fx(i,j)。计算出第i行所有空格的fx后,将第i个皇后放到第i行中那个与前面i-1个皇后不在同一列或对角线上且fx值最大的空格中(相同时取第一个)。
如果当前策略无法求解,则回溯至上一步,选择fx值次大的空格放置皇后,依次类推,直至找到一个合法的解。
#include<stdio.h>
#include <cstdio>
#include<string>
#include<math.h>
#include<stdlib.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<time.h>
#include<list>
using namespace std;

const int n=8;
int c[8];  //c[i]表示第i行的皇后放在第c[i]列
int fx[8][8];  //fx[i][j]表示在i行j列放置皇后后,剩下行中可以放Q的空格数
int ansflag=0; //标记是否已经找到答案
int vis[3][16];  //vis[0][j]表示第j列有无皇后,vis[1][i+j]表示对角线/上的和相同),vis[2][i-j+n]表示对角线\上的差相同,+n避免负数

//启发式函数f():找到剩下行可以放Q的空格数
int f(int row)
{
    int cnt=0;
    for(int i=row+1;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(!vis[0][j]&&!vis[1][i+j]&&!vis[2][i-j+n])
                cnt++;
        }
    }
    return cnt;
}

void search(int cur)
{
    if(cur==n)
        ansflag++;  //所有行都合法的放置了Q,结束
    else
    {
        int flag=0;  //标志该行是否可以放置皇后
        for(int i=0;i<n;i++)  //对于cur行的每个空格进行测试
        {
             if(!vis[0][i]&&!vis[1][cur+i]&&!vis[2][cur-i+n])
             {
                 flag=1;  //[cur][i]处可以放置Q
                 c[cur]=i;
                 vis[0][i]=vis[1][cur+i]=vis[2][cur-i+n]=1;
                 fx[cur][i]=f(cur);  //计算启发式函数
                 vis[0][i]=vis[1][cur+i]=vis[2][cur-i+n]=0;
             }
        }
        if(flag)   //标志该行可以放置皇后
        {
            while(!ansflag)
            {
                int max=-1;
                int col=-1;     //记录fx最大的列
                for(int i=0;i<n;i++) //找fx最大的列
                {
                    if(fx[cur][i]>max)
                    {
                        max=fx[cur][i];
                        col=i;
                    }
                }
                if(max==-1) //在本行任一空格放置皇后都无法求解,回溯
                {
                    fx[cur-1][c[cur-1]]=-1;  //将原来的最大值置为-1,那么下一次回溯找的是次大值。
                    return;
                }
                c[cur]=col;   //找到fx最大的列,放置皇后,搜索下一行。
                vis[0][col]=vis[1][cur+col]=vis[2][cur-col+n]=1;
                search(cur+1);
                vis[0][col]=vis[1][cur+col]=vis[2][cur-col+n]=0;
            }
        }
        else   //标志该行不可以放置皇后
            fx[cur-1][c[cur-1]]=-1;
    }
}

int main()
{
    memset(c,-1,sizeof(c));
    memset(fx,-1,sizeof(fx));
    memset(vis,0,sizeof(vis));
    search(0);
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(j==c[i])
                cout<<"Q"<<‘ ‘;
            else
                cout<<"X"<<" ";
        }
        cout<<endl;
    }
}

解对应的棋盘: 
Q X X X X X X X 
X X X X X Q X X 
X X X X X X X Q 
X X Q X X X X X 
X X X X X X Q X 
X X X Q X X X X 
X Q X X X X X X 
X X X X Q X X X

原文地址:https://www.cnblogs.com/dshn/p/8276289.html

时间: 2024-10-13 22:36:18

八皇后(c++启发式函数求解)的相关文章

通过C语言,利用递归回溯的方法,实现八皇后问题的求解

八皇后问题: 在国际象棋8*8的棋盘上,摆放八个皇后且皇后都无法吃掉对方,而八皇后的攻击路线 为它所在的列和行,还有45度斜线. 对于该问题,首先要确定递归的输入和输出,以及终止条件和方法.一个递归完成对当 前行皇后位置的确定,并通过遍历所有列,查找出所有可能.其中,利用对列的遍历实 现回溯. 具体实现方法,可以通过代码理解,以及思路参考https://blog.csdn.net/a304672343/article/details/8029122 参考博客的博主对于八皇后位置的表示处理的很好,

VC版八皇后

一.  功能需求: 1. 可以让玩家摆棋,并让电脑推断是否正确 2. 能让电脑给予帮助(给出全部可能结果) 3. 实现悔棋功能 4. 实现重置功能 5. 加入点按键音效果更佳 二.  整体设计计: 1.   核心算法: 递归实现(回溯算法): 思路:按行分别安排皇后(Q),Q数目眼下为8. Q1从第一行第一列開始到最后一列,先放在第一列: Q2从第二行第一列到最后一列,每次都检查冲突,不冲突才干够落子: 依次尝试Qm- 假设Qm没有可摆放的位置,则返回Qm-1,同一时候Qm-1放弃刚才的位置:

八皇后(JAVA算法实现)

在学习现代软件工程构建之法这门课时,老师要求发表一篇博客,使用JAVA算法实现八皇后问题的求解.写这篇博客时,我学习了一些其他的博客,因为我常常遇到问题,自己无法解决时,向他人学习也是一种方法. 国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 我们可以逐行或者逐列来进行可行摆放方案的遍历,每一行(或列)遍历出一个符合条件的 位置,接着就到下一行或列遍历下一个棋子的合适位置,这

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

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

求解八皇后问题的退火算法

这个算法收敛速度还算满意.此算法可以计算n皇后问题,只需要将n改为相应整数即可! 主程序: clear; clc; %% %八皇后问题,8X8的棋盘上,放置8个皇后,使之两两都不能攻击 %使用退火算法计算 %初始的状态,随机在棋盘上放置8个皇后,每列放一个,使每一行都不能攻击 n = 8; %8皇后 %% %产生一个随机的状态 state = randperm(n); %% %计算当前状态的h函数值(与目标状态的差距,h越大,差距越大.h=0代表此状态即为目标状态) h = fun_c(stat

八皇后

八皇后(可以扩展为N皇后问题) 每行每列每个对角线都不允许有两个或两个以上的皇后 回溯,递归求解 #include<iostream>/// 八皇后 #include<cstdio> using namespace std; int c[10]; /// 第i行 列为a[i] int total; int n; /// 在一条主对角线上 则它们的 x-y相同 y=x+b /// 在一条负对角线上 则它们的 x+y相同 y=-x+b int v[3][100]; /// v[0]列

【八皇后问题】 回溯算法

回溯算法:回溯算法实际上是一个类似枚举的搜索尝试方法,它的思想是在搜索尝试中寻找问题的解,当发现不满足求解条件时,就“回溯”返回,尝试别的路径.之前介绍的基础算法中的贪婪算法,动态规划等都具有“无后效性”,也就是在分段处理问题时,某状态一旦确定,将不再改变.而多数问题很难找到"无后效性”的阶段划分和相应决策,而是通过深入搜索尝试和回溯操作完成的. 八皇后问题:8*8的国际象棋棋盘中放八个皇后,是任意两个皇后不能互相吃掉.规则:皇后能吃掉同一行,同一列,同一对角线的任意棋子. 模型建立:不妨设八个

kb-01-a&lt;简单搜索--dfs八皇后问题变种&gt;

题目描述: 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C. Input 输入含有多组测试数据. 每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目. n <= 8 , k <= n 当为-1 -1时表示输入结束. 随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域,

poj1321--棋盘问题(搜索练习2,变形的八皇后问题)

棋盘问题 Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Status Appoint description:  System Crawler  (2013-07-31) Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有