【第0003页 · 递归】N皇后问题

news2024/9/21 12:31:13

【前言】本文以及之后的一些题解都会陆续整理到目录中,若想了解全部题解整理,请看这里:

第0003页 · N皇后问题

         今天我们来看一个著名的问题:N皇后问题。在此之前,我们先温习一下递归的思想。当然,温习的方式是看一道题目——全排列。

【全排列】现在给出 1 ~ n 这 n 个数,请问将这 n 个数排成一行共有多少种排法?例如:对于 1 ~ 3,可以有 (1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2,1) 6种排法。

【解题分析】这里我们用递归的思想。构造一个数组 P 用来存储当前排列的情况。再设一个数组 hash 用来标记哪些数已经放置在了当前的排列中,hash = 0 表示未放入,hash = 1 表示已放入。

        现在按照顺序往 P 数组的第 1 位到第 n 位放置数字。不妨假设当前已经填好了 1 ~ i 位,准备填 i + 1 位,那么就需要枚举 1 ~ n,如果这个数的 hash = 0,那么就可以将其放入第 i + 1 位,将其 hash 标记为 1,然后递归处理第 i + 2 位。注意:在这个递归结束后要将这个数的 hash 重新标记为 0。而递归的边界就是到达第 n + 1 位,此时说明 1 ~ n 位都已经填好了,我们定义一个数 cnt 用来记录排列种数。当到达边界时,cnt++。

【源码展示】

#include <cstdio>
using namespace std;
int cnt = 0, P[100] = {0};
bool hash[100] = {false};

void generate(int number, int n) {
    if (number == n + 1) {
        // 代表 1~n 位都排列好了
        cnt++;
        return;
    }
    // 处理当前这一位
    for (int i = 1; i <= n; i++) {
        if (!hash[i]) {
            P[number] = i;
            hash[i] = true;
            generate(number + 1, n);
            hash[i] = false;
        }
    }
}

int main() {
    int n;
    scanf("%d", &n);
    generate(1, n);
    printf("%d", cnt);
    return 0;
}

        复习完递归的知识后,我们来看一下 N 皇后问题。这里我们采用暴力枚举和回溯优化两种做法来实现这个问题。但注意,暴力枚举不能完成 n = 12及以上的情况,这时我们必须要使用回溯优化的解法。(蓝色为OJ平台的链接)

【N皇后问题】给出一个 n × n 的国际象棋棋盘,你需要在棋盘中摆放 n 个皇后,使得任意两个皇后之间不能互相攻击。具体来说,不能存在两个皇后位于同一行、同一列,或者同一对角线。请问共有多少种摆放方式满足条件。 

IO要求示例

输入描述:

一行,一个整数 n (1 <= n <= 12) 表示棋盘大小

8

输出描述:

一行,一个整数 num 表示摆放种类

92

 【解题分析】对于这个问题,最基本的方法就是枚举所有摆放种类,看看有哪些可行,但显然这不是很好的方法。我们换个角度考虑这个问题。

        如上图所示,对于每一列每一行而言都只能有一枚棋子,并且一定有一枚棋子。那么如果我们按照列从左往右编号,对于行从上往下编号,实际上,在不考虑对角线是否符合要求的情况下,这就是枚举所有全排列的情况罢了。例如:对于上面的左图,我们可以认为棋子为 (24135),右图为 (35124)。

        此时,我们就可以在全排列的代码基础上进行修改,在每次到达递归边界时判断是否符合对角线的要求即可。

【源码展示】

// 最多算到 11, 到 12 就爆掉了!!!所以必须优化!!!
#include <cstdio>
#include <cmath>
using namespace std;
int cnt = 0, P[100] = {0};
bool hash[100] = {false}, flag = true;

void generate(int number, int n) {
    if (number == n + 1) {
        flag = true;
        for (int i = 1; i <= n; i++) {
            for (int j = i + 1; j <= n; j++) {
                if (abs(i - j) == abs(P[i] - P[j])) flag = false;
            }
        }
        if (flag) cnt++;
        return;
    }
    for (int i = 1; i <= n; i++) {
        if (!hash[i]) {
            P[number] = i;
            hash[i] = true;
            generate(number + 1, n);
            hash[i] = false;
        }
    }
}

int main() {
    int n;
    scanf("%d", &n);
    generate(1, n);
    printf("%d", cnt);
    return 0;
}

        但是,经过实验,上面的方法最多只能算到 11,算到 12 的时候就已经爆炸了,所以我们不得不寻找一个更为优化的解法。

【解题分析】那么我们应该如何优化呢?在上述过程中,我们是直到将一种排列表示出来后才进行判断对角线是否合理。那么我们是否可以在排列过程中就进行这个过程呢?答案是可以的!我们在每次排当前位置上的棋子时,就可以判断该棋子是否会与之前放置的棋子产生对角线上的冲突,如果这个冲突存在,那么我们直接就结束这种排列方式。这样就可以大大减少程序消耗的时间。同时,如果到达了最后一枚棋子,那么也就意味着排列是成立的!

【源码展示】

// 但这也不是最好的方法,经过实验,n = 15 时,时间就爆炸了。不过已经满足了本题的要求了
#include <cstdio>
#include <cmath>
using namespace std;
int cnt = 0, P[100] = {0};
bool hash[100] = {false}, flag = true;

void generate(int number, int n) {
    if (number == n + 1) {
        cnt++; // 能到达这里一定是合理的
        return;
    }
    for (int i = 1; i <= n; i++) {
        if (!hash[i]) {
            flag = true;
            for (int pre = 1; pre < number; pre++) {
                if (abs(P[pre] - i) == abs(pre - number)) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                P[number] = i;
                hash[i] = true;
                generate(number + 1, n);
                hash[i] = false;
            }
        }
    }
}

int main() {
    int n;
    scanf("%d", &n);
    generate(1, n);
    printf("%d", cnt);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2085432.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阅读笔记:明朝那些事儿人间再无魏忠贤

持续了10多天时间&#xff0c;明朝那些事儿第八部人间再无魏忠贤截止到今天凌晨0&#xff1a;58分看完了&#xff0c;给我印象比较深刻的人物杨涟&#xff0c;努尔哈赤&#xff0c;孙承宗&#xff0c;袁崇焕&#xff0c;魏忠贤&#xff0c;皇太极&#xff0c;熊廷弼&#xff0c…

C#MDI子窗体通过TabControl列表显示的控制实现过程

类似excel表格中各个表单sheet的切换效果&#xff0c;使用tabcontrol控件实现类似的功能。效果如下&#xff1a; 过程涉及父窗体MDIParent1、子窗体main、自定义基础功能类MdiChildBase。 基础功能类MdiChildBase继承自Form创建&#xff0c;定义了一个委托SetTabControlDelega…

项目:基于TCP的文件传输系统

项目介绍: 模拟FTP原理&#xff1a;客户端连接服务器后&#xff0c;向服务器发送一个文件。文件名可以通过参数指定&#xff0c;服务器端接收客户端传来的文件&#xff08;文件名随意&#xff09;&#xff0c;如果文件不存在自动创建文件&#xff0c;如果文件存在&#xff0c;…

2024年整理的自动化测试面试题及答案

selenium中如何判断元素是否存在&#xff1f; 没有提供原生的方法判断元素是否存在&#xff0c;一般我们可以通过定位元素异常捕获的方式判断selenium中hidden或者是display &#xff1d; none的元素是否可以定位到&#xff1f;不可以&#xff0c;想点击的话&#xff0c;可以用…

C# 爬虫技术:京东视频内容抓取的实战案例分析

摘要 随着互联网技术的飞速发展&#xff0c;数据的获取和分析变得愈发重要。爬虫技术作为数据获取的重要手段之一&#xff0c;广泛应用于各个领域。本文将重点探讨C#语言在京东视频抓取中的实现过程&#xff0c;分析其技术细节&#xff0c;并提供相应的代码实现。 引言 京东…

python学习之路 - 面向对象编程

目录 一、面向对象编程1、成员方法a、类的定义和使用b、案例 2、类和对象3、构造方法4、其他内置方法&#xff08;魔术方法&#xff09;5、面向对象三大特性——封装a、介绍&#xff1a;b、表现形式&#xff1a;私有成员变量与私有成员方法c、作用 6、面向对象三大特性——继承…

iview Cascader 组件动态数据回显

在使用Cascader组件动态加载数据后&#xff0c;编辑的时候回显会有问题 问题如下&#xff1a;回显的时候&#xff0c;如果是多级&#xff0c;只显示了一级且&#xff0c;中间会闪一下 经过多方查找资料发现&#xff0c;是callback造成的。给组件增加on-visible-change事件监听…

如何下载淘宝的主图视频

目录&#xff1a; 1、通过插件插件下载短视频 1&#xff09;获取“Microsoft Edge扩展” 2&#xff09;搜索“aix智能下载器” 3&#xff09;将插件钉在浏览器上 4&#xff09;嗅控并下载视频 2、从其他来源安装插件 1、通过插件插件下载短视频 1&#xff09;获取“M…

孙宇晨:以区块链科技为翼,青年企业家引领社会进步新航向

​ 孙宇晨&#xff0c;作为区块链领域的一位青年企业家&#xff0c;以其大胆的创新精神和卓越的远见&#xff0c;正在用区块链技术推动社会的进步。他不仅在加密货币和区块链技术领域取得了令人瞩目的成就&#xff0c;还通过不断的努力&#xff0c;致力于将这些技术应用…

FreeRTOS 列表 List 源码解析

目录 一、链表及链表项的定义1、链表节点数据结构 xList_ITEM2、链表精简节点结构 xMINI_LIST_ITEM3、链表根节点结构 xLIST 二、链表的相关操作1、初始化1.1 链表节点初始化1.2 链表根节点初始化 2、插入2.1 将节点插入到链表的尾部2.2 将节点按照升序排列插入到链表 3、删除4…

(go)线性表的顺序存储

闲来无事&#xff0c;更新一下&#xff0c;线性表的顺序存储&#xff0c;go语言版本&#xff0c;效果都已经测试过&#xff0c;下面给出各部分细节 文章目录 1、生成一个线性表2、查找3、插入4、求长度5、改值6、删除7、遍历8、测试程序9、完整代码总结 package mainimport &q…

VBA技术资料MF195:屏蔽工作表中的粘贴输入

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

关于测试工程师在性能测试工具jmeter的熟悉和精通

经过一周的jmeter接口编写&#xff0c;不不不&#xff0c;是一年1-2次的jmeterd 使用&#xff0c;每次都是新的发现&#xff0c;新的起点&#xff01;&#xff01; 去年10月学习过的东西&#xff0c;现在还是不记得当时怎么这么聪明&#xff0c;于是&#xff0c;每次都是0基础…

笔试训练,牛客.合唱团牛客.kannan与高音牛客.拜访(BFS)牛客.买卖股票的最好时机(四)

目录 牛客.合唱团 牛客.kannan与高音 牛客.拜访&#xff08;BFS&#xff09; 牛客.买卖股票的最好时机(四) 牛客.合唱团 dp[i][j]:从1到i,中挑选最大乘积是多少&#xff0c;但是我们会发现状态转移方程推不出来&#xff0c;我们不知道如何推导的任意两个人&#xff0c; 从[…

[解决]Invalid configuration `aarch64-openwrt-linux‘: machine `aarch64-openwrt

背景 交叉编译libev-4.19 问题 checking host system type… Invalid configuration aarch64-openwrt-linux: machine aarch64-openwrt’ not recognized 解决 打开config.sub&#xff0c;在244行后添加"| aarch64-openwrt \ "

RK 方案u-boot阶段添加驱动

驱动部分&#xff1a; u-boot/drivers/video/drm/gpio_init.c /** (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd** SPDX-License-Identifier: GPL-2.0*/#include <config.h> #include <common.h> #include <errno.h> #include <malloc…

jmeter连接mysql数据,并将查询结果存储到指定txt文件中

1、首先jmeter先进行连接mysql相关的配置&#xff0c;我之前已经有教程了就不赘述了&#xff0c;教程链接如下 jmeter连接mysql数据库以及常规用法-CSDN博客 2、当jmeter成功配置mysql数据库后&#xff0c;在JDBC Request组件中进行如下配置 Variable Name of Pool declared…

关于springboot对接通义千问大模型的尝试

今天正在路上刷手机&#xff0c;突然看到群里有人发了一个链接&#xff0c;内容是Spring Cloud Alibaba AI 的使用&#xff0c;spring cloud AI的使用&#xff0c;于是就想着玩一玩试试&#xff0c;难度不大&#xff0c;就是有些文档的坑&#xff0c;这里做一个记录&#xff0c…

基于RK3588+MCU智能清洁车应用解决方案

智能清洁车应用解决方案 在智慧城市建设发展的过程中&#xff0c;智慧环卫是打造智慧城市的重要组成部分&#xff0c;智能清洁车作为实现环卫智能化、提升作业效率和服务质量的关键工具&#xff0c;发挥着不可或缺的作用。 智能清洁车集成了激光雷达、双目视觉、多重传感器以及…

九月更新|用这5个简单技巧,新手在国内也能轻松使用ChatGPT,GPT新手使用手册(学术教师)

一、 ChatGPT可以做什么&#xff1f; 简单来说&#xff0c;ChatGPT就像一个超级智能的聊天机器人&#xff0c;它可以做很多事情。你可以把它想象成一个非常聪明的助手&#xff0c;随时随地帮你解答问题、提供建议、写文章、甚至讲笑话。以下是几个具体的例子&#xff1a; 1. …