引言

最近在刷leetcode算法题的时候,51题很有意思;

题目是这样的:

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

 

示例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:[["Q"]]

 

提示:

    1 <= n <= 9
    皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这道题有意思地方在哪里,是它需要你算错所有的可能性;而不是其中的1种或者2种可能性;

看到这道题想起只有在游戏内做过的消除算法判定方案;《小游戏五子连珠消除解决方案》在这片文章中,我只是去验算内存中能消除的对象,也就是查找所有已经出现的对象进行判定;

这道题是让你从未知的开始,去填充数据,然后找出所有可能性;

题解1

第一遍读完题目的时候,我盲目的自以为然的写了一个算法;

/**
* @author: Troy.Chen(失足程序员, 15388152619)
* @version: 2021-07-08 10:53
**/
class Solution { public List<List<String>> solveNQueens(int n) {
List<List<String>> ret = new ArrayList<>();
List<boolean[][]> ot = new ArrayList<>();
for (int z = 0; z < n; z++) {
for (int x = 0; x < n; x++) {
boolean[][] action = action(ot, n, x, z);
if (action == null) {
continue;
}
List<String> item = new ArrayList<>();
for (boolean[] booleans : action) {
String str = "";
for (boolean a : booleans) {
str += a ? "Q" : ".";
}
item.add(str);
}
ret.add(item);
}
}
return ret;
} public boolean[][] action(List<boolean[][]> ot, int n, int startX, int startZ) {
boolean[][] tmp = new boolean[n][n];
tmp[startZ][startX] = true;
int qN = 1;
for (int z = 0; z < tmp.length; z++) {
for (int x = 0; x < tmp.length; x++) {
if (check(tmp, x, z)) {
tmp[z][x] = true;
qN++;
}
}
}
if (qN >= n) {
if (!ot.isEmpty()) {
for (boolean[][] tItem : ot) {
boolean check = true;
for (int z = 0; z < tmp.length; z++) {
for (int x = 0; x < tmp.length; x++) {
if (tmp[z][x]) {
if (tmp[z][x] != tItem[z][x]) {
check = false;
break;
}
}
}
if (!check) {
break;
}
}
if (check) {
return null;
}
}
}
ot.add(tmp);
return tmp;
} else {
return null;
}
} public boolean check(boolean[][] tmp, int checkx, int checkz) {
/*检查横向*/
for (int x = 0; x < tmp.length; x++) {
if (tmp[checkz][x]) {
return false;
}
}
/*检查纵向*/
for (int z = 0; z < tmp.length; z++) {
if (tmp[z][checkx]) {
return false;
}
}
int tx;
int tz;
{
/*从左到右,从上到下*/
tx = checkx;
tz = checkz;
while (true) {
if (tmp[tz][tx]) {
return false;
}
tx--;
tz--;
if (tx < 0 || tz < 0) {
break;
}
} tx = checkx;
tz = checkz;
while (true) {
if (tmp[tz][tx]) {
return false;
}
tx++;
tz++;
if (tx >= tmp.length || tz >= tmp.length) {
break;
}
}
}
{
/*从右到左,从上到下*/
tx = checkx;
tz = checkz;
while (true) {
if (tmp[tz][tx]) {
return false;
}
tx++;
tz--;
if (tx >= tmp.length || tz < 0) {
break;
}
}
tx = checkx;
tz = checkz;
while (true) {
if (tmp[tz][tx]) {
return false;
}
tx--;
tz++;
if (tx < 0 || tz >= tmp.length) {
break;
}
}
}
return true;
} }

这个写法,是我脑袋里的第一想法,代码怎么理解呢;

就是说我从第一个位置开始去摆放皇后,然后依次去递推其他格子皇后摆放位置,然后查询皇后数量是否符合提议;自己测试了即便就感觉对了,在leetcode提交代码

提交代码过后啪啪打脸了,失败了

查看执行输出,当输入n=6的时候;

leetcode的输出结果是有4种可能性,但是我输出可能性只有1种;

对比之前五子棋消除方案来讲;check代码无疑是没有问题的,起码我在对于纵向横向,斜45度检查方案是没问题的;能保住输出是符合要求的,

那么问题就在于查找可能性代码;我们能找出符合条件的可能性,只是没有办法去找出所有的可能性

仔细分析了一下,不难看出完整的循环一次,得出的结果其实是固定;

怎么理解呢,因为每一次for循环数值都是从0开始推导;那么找到第一个符合条件的格子就落子了,所以得到的结果总是一致的;

既然我们需要算出所有组合;

那么我们能有什么样的方式去完成这个组合求解呢?

题解2

思考了良久;既然正常的循环其实无法查找每一种可能性,只能放弃正常循环;

思考了一下能不能按照当前行,每一个格子,依次往下逐行去扫描每一种可能性;

如果我们要这样去逐行扫描,我们就得在初始化棋盘格子到时候摆放上,上一行的情况才能去推算出当前行能在那些地方摆放;

当前行的递推的时候也是按照第一个格子到最后一个格子去验算能摆放的格子,然后查找到能摆放的格子,就把现在的快照数据提交给下一行进行推算;

这样的做法就是,当第一行开始判断是,第一个格子肯定能摆放棋子对吧,然后把第一个格子摆放上棋子过后,把棋盘直接推给第二行,

第二行拿到棋盘过后,去查找能摆放的格子,每查找到能摆放的格子就把棋盘拷贝一次副本,然后设置当前查找的格子落子,然后把副本棋盘传递给下一行;

这样依次类推,如果能达到最后一行,并且找到适合的格子,那么就是一种可能性,

我们在递推的同时,每一次都会记录自己的进度,再次往下递推;

这么说可能有些抽象;来看一下图

我们的递推过程就是,从a1开始,每一次找到合适的格子就把棋盘拷贝一次,传递给下一行(b),然后b开始从第一个格子开始递推,找到符合规则的格子就再一次拷贝棋盘传递给C;

这样的话,就是完完整的逐行扫描了,所有的肯能行;

来看一下带实现

    /**
* @author: Troy.Chen(失足程序员, 15388152619)
* @version: 2021-07-08 10:53
**/
class Solution { public List<List<String>> solveNQueens(int n) {
List<List<String>> ret = new ArrayList<>();
List<boolean[][]> ot = new ArrayList<>(); boolean[][] tmp = new boolean[n][n];
check(ot, tmp, 0, 0); for (boolean[][] a : ot) {
ret.add(convert(a));
}
return ret;
} /*按照规定转化字符串*/
public List<String> convert(boolean[][] tmp) {
List<String> item = new ArrayList<>();
for (boolean[] booleans : tmp) {
String str = "";
for (boolean a : booleans) {
str += a ? "Q" : ".";
}
item.add(str);
}
return item;
} public void check(List<boolean[][]> ot, boolean[][] tmp, int checkx, int checkz) {
for (int x = checkx; x < tmp.length; x++) {
if (check0(tmp, x, checkz)) {
/*相当于逐行进行扫描所以要拷贝代码*/
int tmpz = checkz;
boolean[][] clone = clone(tmp); clone[tmpz][x] = true;
tmpz++;
if (tmpz < tmp.length) {
check(ot, clone, 0, tmpz);
} else {
ot.add(clone);
}
}
}
} /*拷贝数组*/
public boolean[][] clone(boolean[][] tmp) {
boolean[][] clone = tmp.clone();
for (int i = 0; i < tmp.length; i++) {
clone[i] = tmp[i].clone();
}
return clone;
} public boolean check0(boolean[][] tmp, int checkx, int checkz) {
/*检查横向*/
for (int x = 0; x < tmp.length; x++) {
if (tmp[checkz][x]) {
return false;
}
}
/*检查纵向*/
for (int z = 0; z < tmp.length; z++) {
if (tmp[z][checkx]) {
return false;
}
}
int tx;
int tz;
{
/*从左到右,从上到下*/
tx = checkx;
tz = checkz;
while (true) {
if (tmp[tz][tx]) {
return false;
}
tx--;
tz--;
if (tx < 0 || tz < 0) {
break;
}
} tx = checkx;
tz = checkz;
while (true) {
if (tmp[tz][tx]) {
return false;
}
tx++;
tz++;
if (tx >= tmp.length || tz >= tmp.length) {
break;
}
}
}
{
/*从右到左,从上到下*/
tx = checkx;
tz = checkz;
while (true) {
if (tmp[tz][tx]) {
return false;
}
tx++;
tz--;
if (tx >= tmp.length || tz < 0) {
break;
}
}
tx = checkx;
tz = checkz;
while (true) {
if (tmp[tz][tx]) {
return false;
}
tx--;
tz++;
if (tx < 0 || tz >= tmp.length) {
break;
}
}
}
return true;
} }

再次提交代码;

代码执行通过了,得到的结论也是一样的了;

但是不幸的是我的代码只击败了8%的用户,希望得到各位园友性能更高效的算法;

总结

这样的递推方式,其实可以实现像五子棋AI算法;通过落子情况去递推在哪里下子可以得到五个棋子在一条线上;

只是递推开上时棋盘已经定下了落子情况;从当前的落子情况去递推他的可能性;

递推算法,AI衍生的更多相关文章

  1. 数据结构与算法之递推算法 C++与PHP实现

    数据结构是算法实现的基础,算法总是要依赖于某种数据结构来实现的.往往是在发展一种算法的时候,构建了适合于这样的算法的数据结构.一种数据结构假设脱离了算法,也就没有存在的价值了. 算法的作用----解决 ...

  2. 穷举算法和递推算法(Java)

    穷举算法 概念: 最简单算法,依赖计算机的强大计算能力穷尽每一种可能的情况.穷举算法效率不高,但是适合一些没有明显规律可循的场合. 思想: 在使用穷举算法时,需要明确问题答案的范围,这样才可能在指定范 ...

  3. 求逆元的两种方法+求逆元的O(n)递推算法

    到国庆假期都是复习阶段..所以把一些东西整理重温一下. gcd(a,p)=1,ax≡1(%p),则x为a的逆元.注意前提:gcd(a,p)=1; 方法一:拓展欧几里得 gcd(a,p)=1,ax≡1( ...

  4. c语言-递推算法1

    递推算法之一:倒推法 1.一般分析思路: if 求解初始条件F1 then begin { 倒推 } 由题意(或递推关系)确定最终结果Fn; 求出倒推关系式Fi-1 =G(Fi ); i=n; { 从 ...

  5. Re.常系数齐次递推

    前言 嗯   我之前的不知道多少天看这个的时候到底在干什么呢 为什么那么..  可能大佬们太强的缘故 最后仔细想想思路那么的emmm 不说了  要落泪了 唔唔唔 前置 多项式求逆 多项式除法/取模 常 ...

  6. 算法笔记_091:蓝桥杯练习 递推求值(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 已知递推公式: F(n, 1)=F(n-1, 2) + 2F(n-3, 1) + 5, F(n, 2)=F(n-1, 1) + 3F(n- ...

  7. 从三数之和看如何优化算法,递推-->递推加二分查找-->递推加滑尺

    人类发明了轮子,提高了力的使用效率. 人类发明了自动化机械,将自己从重复的工作中解脱出来. 提高效率的方法好像总是离不开两点:拒绝无效劳动,拒绝重复劳动.人类如此,计算机亦如是. 前面我们说过了四数之 ...

  8. LG5487 【模板】线性递推+BM算法

    [模板]线性递推+BM算法 给出一个数列 \(P\) 从 \(0\) 开始的前 \(n\) 项,求序列 \(P\) 在\(\bmod~998244353\) 下的最短线性递推式,并在 \(\bmod~ ...

  9. Berlekamp Massey算法求线性递推式

    BM算法求求线性递推式   P5487 线性递推+BM算法   待AC.   Poor God Water   // 题目来源:ACM-ICPC 2018 焦作赛区网络预赛 题意   God Wate ...

随机推荐

  1. 标准Gitlab命令行操作指导

    gitlab是一个分布式的版本仓库,总比只是一个本地手动好些,上传你的本地代码后后还能web GUI操作,何乐不为? 贴上刚刚搭建的gitlab,看看git 如何操作标准命令行操作指导 1.命令行操作 ...

  2. 记go中一次http超时引发的事故

    记一次http超时引发的事故 前言 分析下具体的代码实现 服务设置超时 客户端设置超时 http.client context http.Transport 问题 总结 参考 记一次http超时引发的 ...

  3. Jmeter- 笔记12 - 性能测试分析 & 性能测试流程

    性能测试分析 场景设计.监视图表: 设计场景:阶梯式.波浪式 监视器: 收集用于性能分析的数据:TPS图表.聚合报告\汇总报告.察看结果树.响应时间.吞吐量 服务器资源监控:cpu.内存.磁盘io 分 ...

  4. Python+Selenium学习笔记6 - 定位

    1.8种针对单个元素的定位方法 find_element_by_id() find_element_by_name() find_element_by_class_name() find_elemen ...

  5. 如何写新的C++ OP

    如何写新的C++ OP 概念简介 简单介绍需要用到基类,详细介绍请参考设计文档. framework::OperatorBase: Operator(简写,Op)基类. framework::OpKe ...

  6. 编译ONNX模型Compile ONNX Models

    编译ONNX模型Compile ONNX Models 本文是一篇介绍如何使用Relay部署ONNX模型的说明. 首先,必须安装ONNX包. 一个快速的解决方案是安装protobuf编译器,然后 pi ...

  7. 1-3. SpringBoot基础,Java配置(全注解配置)取代xml配置

    最近突发奇想,整合一下以前一些学习笔记,分享自己这几年爬过的坑,逐步更新文章,谢谢大家的关注和支持. 这节讲一下SpringBoot的学习必须的一些基础,Java配置.其实在Spring2.0时代就已 ...

  8. mybatis在动态 SQL 中使用了参数作为变量,必须要用 @Param 注解

    如果在动态 SQL 中使用了参数作为变量,那么就要用 @Param 注解,即使你只有一个参数.如果我们在动态 SQL 中用到了 参数作为判断条件,那么也是一定要加 @Param 注解的,例如如下方法: ...

  9. Python 机器学习实战 —— 监督学习(上)

    前言 近年来AI人工智能成为社会发展趋势,在IT行业引起一波热潮,有关机器学习.深度学习.神经网络等文章多不胜数.从智能家居.自动驾驶.无人机.智能机器人到人造卫星.安防军备,无论是国家级军事设备还是 ...

  10. nacos 实战(史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...