递推算法,AI衍生
引言
最近在刷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衍生的更多相关文章
- 数据结构与算法之递推算法 C++与PHP实现
数据结构是算法实现的基础,算法总是要依赖于某种数据结构来实现的.往往是在发展一种算法的时候,构建了适合于这样的算法的数据结构.一种数据结构假设脱离了算法,也就没有存在的价值了. 算法的作用----解决 ...
- 穷举算法和递推算法(Java)
穷举算法 概念: 最简单算法,依赖计算机的强大计算能力穷尽每一种可能的情况.穷举算法效率不高,但是适合一些没有明显规律可循的场合. 思想: 在使用穷举算法时,需要明确问题答案的范围,这样才可能在指定范 ...
- 求逆元的两种方法+求逆元的O(n)递推算法
到国庆假期都是复习阶段..所以把一些东西整理重温一下. gcd(a,p)=1,ax≡1(%p),则x为a的逆元.注意前提:gcd(a,p)=1; 方法一:拓展欧几里得 gcd(a,p)=1,ax≡1( ...
- c语言-递推算法1
递推算法之一:倒推法 1.一般分析思路: if 求解初始条件F1 then begin { 倒推 } 由题意(或递推关系)确定最终结果Fn; 求出倒推关系式Fi-1 =G(Fi ); i=n; { 从 ...
- Re.常系数齐次递推
前言 嗯 我之前的不知道多少天看这个的时候到底在干什么呢 为什么那么.. 可能大佬们太强的缘故 最后仔细想想思路那么的emmm 不说了 要落泪了 唔唔唔 前置 多项式求逆 多项式除法/取模 常 ...
- 算法笔记_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- ...
- 从三数之和看如何优化算法,递推-->递推加二分查找-->递推加滑尺
人类发明了轮子,提高了力的使用效率. 人类发明了自动化机械,将自己从重复的工作中解脱出来. 提高效率的方法好像总是离不开两点:拒绝无效劳动,拒绝重复劳动.人类如此,计算机亦如是. 前面我们说过了四数之 ...
- LG5487 【模板】线性递推+BM算法
[模板]线性递推+BM算法 给出一个数列 \(P\) 从 \(0\) 开始的前 \(n\) 项,求序列 \(P\) 在\(\bmod~998244353\) 下的最短线性递推式,并在 \(\bmod~ ...
- Berlekamp Massey算法求线性递推式
BM算法求求线性递推式 P5487 线性递推+BM算法 待AC. Poor God Water // 题目来源:ACM-ICPC 2018 焦作赛区网络预赛 题意 God Wate ...
随机推荐
- xml 解析之 JDOM解析
JDOM 是一种使用 XML 的独特 Java 工具包,用于快速开发 XML 应用程序.JDOM 是一个开源项目,它基于树形结构,利用纯 Java 的技术对 XML 文档实现解析.生成.序列化及多种操 ...
- Git 系列教程(14)- 远程分支
远程分支 远程引用是对远程仓库的引用(指针),包括分支.标签等等 你可以通过 git ls-remote <remote> 来显式地获得远程引用的完整列表 polo@B-J5D1MD6R- ...
- 使用指定源安装python包
对于经常需要按照那个python包的同学,外网下载比较慢的话,可以使用公司内部的镜像进行安装 eg: pip install django -i http://mirrors.***.com.cn/p ...
- [leetcode] 44. 通配符匹配(Java)(动态规划)
44. 通配符匹配 动态规划 做动态规划很简单,三步走: 第一步,判断可否用动态规划做,即判断是否满足两个条件:①最优子结构,②重叠子问题.显然该题求s与p是否match,可由其字串层层分解上来. 我 ...
- 内建函数 iter()
可以选择接受一个无参的可调用对象以及一个哨兵(结束)作为输入.当已这种方式使用时,iter()会创建i一个迭代器,然后重复调用用户提供的可调用对象,直到返回哨兵的值为止. import sys wit ...
- NNVM Compiler,AI框架的开放式编译器
NNVM Compiler,AI框架的开放式编译器 深度学习已变得无处不在且不可或缺.在多种平台(例如手机,GPU,IoT设备和专用加速器)上部署深度学习工作负载的需求不断增长.宣布了TVM堆栈,以弥 ...
- TensorRT 基于Yolov3的开发
TensorRT 基于Yolov3的开发 Models Desc tensorRT for Yolov3 https://github.com/lewes6369/TensorRT-Yolov3 Te ...
- Nsight Compute Profilier 分析
profiler报告包含每次内核启动分析期间收集的所有信息.在用户界面中,它包含一个包含常规信息的标题,以及用于在报告页面或单个收集的启动之间切换的控件.默认情况下,报告以选定的详细信息页面开始. 页 ...
- JavaScript 中精度问题以及解决方案
JavaScript 中的数字按照 IEEE 754 的标准,使用 64 位双精度浮点型来表示.其中符号位 S,指数位 E,尾数位M分别占了 1,11,52 位,并且在 ES5 规范 中指出了指数位E ...
- 编译原理-NFA转化成DFA
1.假定NFA M=<S,∑,f,S0,F> 对M的状态转换图进行以下改造: ①引进新的初态结点X和终态结点Y, X,Y∈S, 从X到S0中的任意结点连一条ε箭弧, ...