递推算法,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 ...
随机推荐
- Kubernetes集群搭建 ver1.20.5
目录 部署方式 1. 基础环境准备 1.1 基础初始化 1.2 安装docker 2. 部署harbor及haproxy高可用反向代理 2.1 镜像加速配置 2.2 高可用master可配置 3. 初 ...
- android Room数据库仓库模式
- 我的Java资料小栈-START
我的Java资料小栈 前言 在学习Java的这一路中,其实说句实话,自己还是看了很多培训结构出的Java资料,有时候个人觉得培训结构有的东西还是讲的比较通俗易懂的,又想着有些最新的或者个人有时候需要及 ...
- 用golang刷LeetCode
用golang刷LeetCode 用Go语言刷LeetCode记录,只是为了练习Go语言,能力有限不保证都是最优解,只能在此抛转引玉了. 数据结构和算法 数据结构和算法是程序员的命根子,没了命根子也就 ...
- ImportError:no mudle named 'cv2'
提供一下下载的网址:OpenCV,速度比较慢. 我的anaconda版本Python是3.6的,直接提供百度云下载: 链接:https://pan.baidu.com/s/1Xz9JrE2m-dwPv ...
- 自监督学习(Self-Supervised Learning)多篇论文解读(下)
自监督学习(Self-Supervised Learning)多篇论文解读(下) 之前的研究思路主要是设计各种各样的pretext任务,比如patch相对位置预测.旋转预测.灰度图片上色.视频帧排序等 ...
- CEVA引入新的可配置传感器集线器DSP架构
CEVA引入新的可配置传感器集线器DSP架构 CEVA introduces new configurable sensor hub DSP architecture 在一个将多个传感器设计成几乎所有 ...
- springmvc自定义的拦截器以及拦截器的配置
一.自定义拦截器 Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口. 二.HandlerIn ...
- 【零基础学深度学习】动手学深度学习2.0--tensorboard可视化工具简单使用
1 引言 老师让我将线性回归训练得出的loss值进行可视化,于是我使用了tensorboard将其应用到Pytorch中,用于Pytorch的可视化. 2 环境安装 本教程代码环境依赖: python ...
- SpringCloud Alibaba实战(5:子模块基本业务开发)
源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 在上一节里,我们搭建了一个微服务项目的整体架构,并进行了版本控制. 接下来我们进一步 ...