递推算法,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 ...
随机推荐
- unity UGUI填坑 之 HorizontalLayoutGroup 和 ContentSizeFitter配合使用
今天在项目中遇到一个问题,我们的ui过来找我,问为什么Content里的Item显示的不完全 花了半个小时看了一下,发现个小小的坑,记录一下 这些属性是用来实现,Content下的Item的偏移和间隔 ...
- tensorflow-yolov4实施方法
tensorflow-yolov4实施方法 tensorflow-yolov4-tflite YOLOv4: Optimal Speed and Accuracy of Object Detectio ...
- LCD: 2D-3D匹配算法
LCD: 2D-3D匹配算法 标题:LCD:Learned Cross-Domain Descriptors for 2D-3D Matching 作者:Quang-Hieu Pham, Mikael ...
- TensorRT 7.2.1开发初步
TensorRT 7.2.1开发初步 TensorRT 7.2.1开发人员指南演示了如何使用C ++和Python API来实现最常见的深度学习层.它显示了如何采用深度学习框架构建现有模型,并使用该模 ...
- 如何在小型pcb的移动设备上获得更好的无线性能
如何在小型pcb的移动设备上获得更好的无线性能 How to get better wireless performance for mobile devices with small PCBs 小型 ...
- Spring Cloud系列(五):服务网关Zuul
在前面的篇章都是一个服务消费者去调用一个服务提供者,但事实上我们的系统基本不会那么简单,如果真的是那么简单的业务架构我们也没必要用Spring Cloud,直接部署一个Spring Boot应用就够了 ...
- 移动通信-5G
1.移动通信的发展历程: "G"代表一代,每10年一个周期 1G 2G 3G 4G 5G 1980s 1990s 2000s 2010s 2020s 语音 短信 社交应用 在线.互 ...
- 单点突破:Set
HashSet HashSet存放的是散列值,它是按照元素的散列值来存取元素的. 元素的散列值通过hashCode方法计算 HashSet通过判断两个元素的Hash值是否相等,如果相等就会用equal ...
- Redis哨兵的配置和原理
哨兵 在一个典型的一主多从的Redis系统中,当主数据库遇到异常中断服务后,需要手动选择一个从数据库升级为主数据库,整个过程需要人工介入,难以自动化. Redis2.8提供了哨兵2.0(2.6提供了1 ...
- Vim一直学不会?试试这个 "真香" 神器
Vim 的使用,一直以来是一个难题 以至于国外的知名程序员问答社区 StackOverFlow 上有一个问题 How to I exit the Vim editor 获得了超过 200万次的浏览量 ...