码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-09-03 10:04:28      阅读:130      评论:0      收藏:0      [点我收藏+]

标签:||   下一步   pac   递归调用   inline   return   cpp   oid   break   

题目来源:《信息学奥赛一本通》例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;
}

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

标签:||   下一步   pac   递归调用   inline   return   cpp   oid   break   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!