搜索入门练习题5 八皇后问题 题解

题目来源:《信息学奥赛一本通》例5.4

题目描述

要在国际象棋棋盘(\(8 \times 8\) 的棋盘)中放 \(8\) 个皇后,使任意两个皇后都不能互相吃。(提示:皇后能吃同一行、同一列、同一对角线的任意棋子。)

输出格式

输出一个整数,用于表示八皇后问题的放置方案。

题目分析

首先我们用 \((x,y)\) 来表示棋盘上第 \(x\) 行第 \(y\) 列的格子的坐标。
那么,两个皇后 \((x_1,y_1)\) 和 \((x_2,y_2)\) 会互相攻击当且仅当满足如下条件之一:

  • 在同一行:\(x_1 = x_2\)
  • 在同一列:\(y_1 = y_2\)
  • 在同一对角线:\(x_1-x_2=y_1-y_2\) 或 \(x_1-x_2=y_2-y_1\) ,化简一下就是坐标差的绝对值相等,即: \(\vert x_1-x_2 \vert = \vert y_1-y_2 \vert\)

我们可以用深度优先搜索来解决这道题。
我们可以发现的是,要想在 \(8 \times 8\) 的棋盘上放置 \(8\) 个皇后,每一行都必须且只能放置一个皇后,所以我开一个数组 ans[]ans[id] 用于表示在第 id 行放置的皇后的列号。
然后我开一个函数 f(id) ,用于表示:当前正准备在第 id 行放置一个皇后。然后我从 \(1\) 到 \(8\) 去遍历列号 \(i\) ,如果 \((id,i)\) 能放,则我尝试性地在 \((id,i)\) 位置放上皇后,然后递归调用 f(id+1) , 直到 id>8 (说明找到了一种放置方案)为止。
那么,我怎么去判断 \((id,i)\) 这个位置能否放置一个皇后呢?我们搜索的顺序是从第 \(1\) 行一直到第 \(8\) 行的,所以当我要放置第 \(id\) 行的皇后的时候,肯定已经在第 \(1\) 到 \(id-1\) 行放置好了前 \(id-1\) 个皇后。所以我只需要从 \(1\) 到 \(id-1\) 去遍历行号 \(j\) ,比较一下第 \(j\) 行放置的皇后 \((j,ans[j])\) 和我现在想要放的皇后 \((id,i)\) 是否会相会攻击即可。
如果这 \(id-1\) 个皇后都和 \((id,i)\) 位置没有冲突,则说明 \((id,i)\) 这个位置是可以放的,那我尝试性地在 \((id,i)\) 位置放上皇后,并递归地进行下一步搜索 f(id+1)
实现代码如下:

#include <bits/stdc++.h>
using namespace std;
int ans[9], cnt; // ans[i]用于记录第i行皇后列号,cnt用于记录方案数
// attack函数用于判断(x1,y1)和(x2,y2)两个点是否会互相攻击
// 返回true:会互相攻击到;返回false:不会互相攻击到
bool attack(int x1, int y1, int x2, int y2) {
    return x1==x2||y1==y2||abs(x1-x2)==abs(y1-y2);
}
// f函数用于在第id行尝试性地放一个i,然后递归地去id+1行放
void f(int id) {
    if (id > 8) {   // 说明前8行已经放好了
        cnt ++;     // 找到一个方案,cnt++
        return;     // 程序可直接返回
    }
    for (int i = 1; i <= 8; i ++) { // 尝试在第id行第i列放皇后
        bool flag = true;   // flag用于标识是否能放
        for (int j = 1; j < id; j ++) {
            if (attack(id, i, j, ans[j])) { // (id,i)和(j,ans[j])冲突
                flag = false;              // 将flag设为false标识不能放
                break;
            }
        }
        if (flag) { // 如果循环结束flag仍为true说明i能放
            ans[id] = i;    // 能放就先放上
            f(id+1);        // 然后递归进行下一行的放置
        }
    }
}
int main() {
    f(1);   // 从第1行开始放
    cout << cnt << endl;    // 输出方案数
    return 0;
}

原文地址:https://www.cnblogs.com/zifeiynoip/p/11450705.html

时间: 2024-08-03 06:12:05

搜索入门练习题5 八皇后问题 题解的相关文章

搜索入门练习题4 数的拆分 题解

题目描述 任何一个大于 \(1\) 的自然数 \(n\) ,总可以拆分成若干个小于 \(n\) 的自然数之和.当 \(n = 4\) 时,总共有 \(4\) 种拆分方法: \(4=1+1+1+1\) \(4=1+1+2\) \(4=1+3\) \(4=2+2\) 现在给你一个数 \(n(1 \le n \le 20)\) ,请按顺序输出 \(n\) 的所有拆分方案. 输入格式 输入包含一个整数 \(n(1 \le n \le 20)\) . 输出格式 输出 \(n\) 的所有拆分方案,每种方案占

搜索入门练习题7 最高效益和 题解

题目出处:<信息学奥赛一本通>例5.6 题目描述 设有A.B.C.D.E五人从事J1.J2.J3.J4.J5五项工作,每人只能从事一项,他们的效益如下所示. ? J1 J2 J3 J4 J5 A 13 11 10 4 7 B 13 10 10 8 5 C 5 9 7 7 4 D 15 12 10 11 5 E 10 11 8 8 4 每人选择五项工作中的一项,在各种选择的组合中,找到效益最高的一组输出. 题目分析 这道题目其实就是"全排列"问题的变形题,我们可以使用深度优先

搜索入门练习题2 全排列 题解

题目出处:课程=>搜索1=>题目A 题目描述 给定一个正整数 \(n\) ,按照递增顺序打印数字 \(1\) 到 \(n\) 的所有排列. 输入格式 一个整数 \(n(1 \le n \le 7)\) . 输出格式 按照递增顺序输出 \(n\) 个数的所有排列,每行代表一组排列, \(n\) 个数两两之间有一个空格分隔. 样例输入 3 样例输出 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 问题分析 这是一道搜索的题目. 我们知道搜索就是状态到状态之间的转换,其本质是

搜索入门练习题3 全组合 题解

题目出处:<信息学奥赛一本通>例5.2. 题目描述 设有 \(n\) 个数的集合 \(\{1,2,...,n\}\) ,从中任意取出 \(r\) 个数进行排列 \((r \le n)\) ,试列出所有的排列. 输入格式 输入包含两个正数 \(n,r(1 \le r \le n \le 10)\) 输出格式 输出从 \(n\) 个数的集合中选出 \(r\) 个数的所有组合,每个组合方案占一行.对于每个组合,按照从小到大的顺序输出组合中的所有元素,两两之间有一个空格分隔. 样例输入 3 2 样例输

搜索入门练习题1 素数环 题解

题目出处:<信息学奥赛一本通>例5.1. 题目描述 素数环:从 \(1\) 到 \(n(2 \le n \le 20)\) 这 \(n\) 个数摆成一个环,要求相邻的两个数的和是一个素数. 输入格式 输入包含一个整数 \(n(2 \le n \le 20)\) . 输出格式 按字典序从小到大的顺序输出所有排列方案,每个排列方案占一行.每行的 \(n\) 个数之间由一个空格分隔. 样例输入 2 样例输出 1 2 2 1 问题分析 很明显,这是一道可以用搜索解决的问题,我们可以采用"回溯

【基础算法】回溯法与八皇后问题

在国际象棋中,皇后是最强大的一枚棋子,可以吃掉与其在同一行.列和斜线的敌方棋子.比中国象棋里的车强几百倍,比她那没用的老公更是强的飞起(国王只能前后左右斜线走一格).上图右边高大的棋子即为皇后. 八皇后问题是这样一个问题:将八个皇后摆在一张8*8的国际象棋棋盘上,使每个皇后都无法吃掉别的皇后,一共有多少种摆法?此问题在1848年由棋手马克斯·贝瑟尔提出,岂止是有年头,简直就是有年头,82年的拉菲分分钟被秒的渣都不剩. 八皇后问题是典型的回溯法解决的问题,我们以这个问题为例介绍回溯法. 所谓回溯法

回溯法和八皇后问题

一.回溯法 回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 二.八皇后问题 (一)问题描述 在国际象棋中,皇后是最强大的一枚棋子,可以吃掉与其在同一行.列和斜线的敌方棋子.比中国象棋里的车强几百倍,比她那没用的老公更是强的飞起(国王只能前后左右斜线走一格). 八皇后问题是这样一个问题:将八个皇后摆在一张8*

洛谷 P1219 八皇后 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1219 题目描述 检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行.每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子. 上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下: 行号 1 2 3 4 5 6 列号 2 4 6 1 3 5 这只是跳

深度搜索(dfs)+典型例题(八皇后)

深度优先搜索简称深搜,从起点出发,走过的点要做标记,发现有没走过的点,就随意挑一个往前走,走不了就回退,此种路径搜索策略就称为“深度优先搜索”,简称“深搜”. 如上面的图所示:加入我们要找一个从V0到V6的一条最短的路径.我们可以看到有许多的路我们可以走. V0——V3——V5——V6: V0——V3——V1——V4: V0——V3——V1——V2——V6: V0——V1——V4: V0——V1——V3——V5——V6: V0——V1——V2——V6: V0——V2——V6: 前两组,是从节点V