八皇后算法解析

article/2025/9/15 9:53:54

今天研究力扣的一道题死活写不出来对应的算法,没办法自己算法基础太差。于是看了下答案,发现使用什么回溯算法,菜鸟表示平时开发期间写的最复杂的程序就是写了两层for循环,已经很牛逼了有木有?这个回溯算法什么鬼?于是乎百度了下,算是了解了回溯算法是什么玩意儿。这里分析一波八皇后算法来加深一下理解。

八皇后算法描述如下:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法!
在这里插入图片描述

下面来分析一波,假设此时我们想要在黑色方块位置放置一个皇后
在这里插入图片描述
如果一列一列的放置皇后的话,图中黑色位置能放置一个皇后的合法性条件为:
1、绿色线条经过的方格没有皇后 (不处于同一斜线)
2、红色线条经过的方格没有皇后 (不处于同一行)
3、紫色线条经过的方格没有皇后 (不处于同一斜线)

也就是说如果以黑色方块位置为参照原点:(0,0)坐标点,紫色和绿色两个线条分别是斜率为1和-1的两个函数,如下图:
紫色线所代表的函数是:y = -x;
绿色先所代表的函数是:y=x;
(横坐标是列,纵坐标为行,注意行从上到下递增)
在这里插入图片描述
凡是位于这两条函数线上的位置(点)以及横坐标(说明位于同一行)都不能有皇后

所以假设某一列皇后的位置用行来记录,比如queen[column] = row,意思是第column列的皇后的位置在第row行。
同行的逻辑很好判断,那么我们想要在黑色方块位置放置一个皇后,怎么判断前面几列是否在绿色线条和紫色线条上已经有了皇后呢?思路也很简单:
假设黑色方块的位置为n列,nRow行,假设位于m列的所在的行是否有皇后位于紫色线或者绿色上,那么就符合下面条件:

//假设此时即将在n列放置一个皇后,n>m]//获取m列上皇后所在的行
int mRow = queen[m]
int nRow = queen[n];
//行的差值
int rowDiff = nRow - mRow;//列的差值
int columnDiff = n-m;

上面代码中 rowDiff的绝对值等于columnDiff的绝对值的话,说明点位于y=x或者y=-x的函数线上:
在这里插入图片描述
就说明此时黑色方块的位置是不能放置皇后的,因为在紫色或者绿色线上已经有了皇后。

那么用代码来(currentColumn,curreentRow)是否可以放置皇后的方法如下

	/*** 判断当(currentRow,currentColumn)是否可以放置皇后* @param currentColumn * @return*/public boolean isLegal(int currentRow,int currentColumn) {//遍历前面几列for(int preColumn=0;preColumn<currentColumn;preColumn++) {int row = queen[preColumn];//说明在子preColumn的低currentRow已经有了皇后if(row==currentRow) {return false;}//行与行的差值int rowDiff= Math.abs(row -currentRow);//列于列的差值int columnDiff =  Math.abs(currentColumn-preColumn);//说明斜线上有皇后if(rowDiff==columnDiff ){return false;}}//end for//说明(currentRow,currentColumn)可以摆放。return true;}}

因为博主是按照一列一列的方式来进行放置的,所以整体思路就是:在当前列逐步尝试每一行是否可以放置皇后,如果有一个可以放置皇后,就继续查看下一列的每一行是否可以放置皇后。所以代码如下:

	 int queen[] = new int[8];int count = 0;private void eightQueen(int currentColumn) {//这个for循环的目的是尝试讲皇后放在当前列的每一行for(int row=0;row<8;row++) {//判断当前列的row行是否能放置皇后if(isLegal(row,currentColumn)) {//放置皇后queen[currentColumn] = row;if(currentColumn!=7) {//摆放下一列的皇后eightQueen(currentColumn+1);}else {//递归结束,此时row要++了count++;}}}//end for}

需要注意的是当currentColumn==7的时候,说明此时已经完成了一种摆放方法,然后for循环继续执行,去尝试其他摆放方法。
测试一波,一共有92种摆放方法:

   public static void main(String args[]) {Queen queen = new Queen();queen.eightQueen(0);System.out.println("总共有 " +queen.count+ " 摆放方法");}

所以结合八皇后的实现来看看到底什么是回溯算法,看百度百科解释:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法

比如八皇后算法来说,我们根据约束条件判断某一列的某一行是否可以放置皇后,如果不可以就继续判断当前列的下一行是否可以放置皇后.如果可以放置皇后,就继续探寻下一列中可以放置皇后的那个位置。完成一次摆放后。再重新挨个尝试下一个可能的摆放方法。

下面用一个力扣的题再次巩固下回溯算法的应用。该题描述如下:

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。说明:所有数字(包括 target)都是正整数。解集不能包含重复的组合。 
示例 1:输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[[7],[2,2,3]
]示例 2:输入: candidates = [2,3,5], target = 8,
所求解集为:
[[2,2,2,2],[2,3,3],[3,5]
]

做该题的重要条件是无重复的数组,那么问题就很好解了。
首先对数组从大到小排序。这是解题的关键。
然后为了减少不必要的遍历,我们要对原来的数组进行截取:

      List<List<Integer>> res = new ArrayList<>();//重要的要大小排列Arrays.sort(candidates);//说明原数组中就没有满足target的数if (candidates[0] > target) {return res;}List<Integer> newCandidates= new ArrayList<Integer>();int len = candidates.length;// 取小于target的数 组成一个临时数组for (int i = 0; i < len; i++) {int num = candidates[i];if (num > target) {break;}newCandidates.add(num);} // end for

通过上面的步骤我们拿到了一个从小到大排列的无重复数组newCandidates,数组中的元素都<=target.
因为数组从小到大排列,所以我们有如下几种情况,以candidates = [2,3,5], target = 8为例:
符合条件的子数组满足条件如下
1、target循环减去一个数,如果能一直减到到差值等于0,那么这个数组成的数组就是一个解,比如[2,2,2,2];
2、target减去一个数,然后形成了一个新的newTarget=target-num[i],让这个newTarget减去下一个数num[i+1],然后执行步骤1,则又是一个解,比如[2,3,3];(其实步骤1是步骤2的一个特例)
3、target减去一个数,然后形成了一个新的newTarget=target-num[i],让这个newTarget减去下一个数num[i+1],如果能一直减到到差值等于0说明又是一个解.,比如[3,5];
如此得到了一个规律,只要是相减之后得到差值=0,就说明就得到一个解。
得到一个新的解之后继续循环数组中的下一个数字,继续执行1,2,3步骤即可。
所以完成的解法如下:

class Solution {public List<List<Integer>> combinationSum(int[] candidates, int target) {List<List<Integer>> res = new ArrayList<>();//重要的要大小排列Arrays.sort(candidates);List<Integer> temp = new ArrayList<Integer>();if (candidates[0] > target) {return res;}int len = candidates.length;// 取小于target的数 足证一个临时数组for (int i = 0; i < len; i++) {int num = candidates[i];if (num > target) {break;}temp.add(num);} // end for//find(res, new ArrayList<>(), temp, target, 0);return res;}public void find(List<List<Integer>> res, List<Integer> tmp, List<Integer> candidates, int target, int start){//target==0.找到一个新的解if (target == 0) {res.add(new ArrayList<>(tmp));}else if(target>0){for (int i = start; i < candidates.size(); i++) {int num = candidates.get(i);if(num<=target){               tmp.add(num);//查找新的targetint newTarget = target-num;find(res, tmp, candidates, newTarget, i);tmp.remove(tmp.size() - 1);}}//end for}}
}

http://chatgpt.dhexx.cn/article/oO2SUXtG.shtml

相关文章

递归算法之八皇后问题

八皇后问题&#xff08;英文&#xff1a;Eight queens&#xff09;&#xff0c;是由国际象棋棋手马克斯贝瑟尔于1848年提出的问题&#xff0c;是回溯算法的典型案例。 问题表述为&#xff1a;在88格的国际象棋上摆放8个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个…

八皇后问题4种c语言算法

八皇后问题 1.递归回溯法 B站懒猫老师讲的&#xff08;我在这里学的&#xff09; 八皇后问题的递归回溯算法思路&#xff1a;从第一行开始当某一行皇后位置不与前面所有皇后位置冲突那么记录该行皇后位置并调用递归函数进入下一行&#xff0c;摆放下一个皇后&#xff0c;逐个…

数据结构与算法 — 八皇后问题(回溯算法)

问题描述 在88格的国际象棋上摆放8个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上&#xff0c;问有多少种摆法。 假设上图中红点为一个皇后的位置&#xff0c;那么他的同行&#xff0c;列&#xff0c;斜线上都不能再放置…

数据结构与算法-- 八皇后问题(多种实现方案)

八皇后问题解法一(排列筛选法) 本篇我们承接上一篇中的思想&#xff0c;想到了一个经典的算法题&#xff0c;八皇后问题&#xff1a;题目&#xff1a;在8*8的国际象棋上摆放8个皇后&#xff0c;使得其互相不能攻击&#xff0c;即任意两个换后不能在同一行&#xff0c;同一列&a…

C语言开方问题求助

求助&#xff1a; 使用的是Devcpp进行的编程尝试 在写程序的时候遇到了个问题&#xff0c;运行后结果显示为1#j 后来在朋友的帮助下改正了程序&#xff0c;解决了错误。之后的话&#xff0c;我按照我朋友的改正过的程序自己再重新打了一遍&#xff0c;结果运行后的结果还是显示…

C语言笔记

目录 基础... 3 基本类型数据... 5 定义变量... 6 进制... 6 ASCII&#xff08;阿斯克码&#xff09;... 7 输入输出... 8 printf&#xff08;&#xff09;将变量的内容输出到显示器上... 8 scanf &#xff08;&#xff09;[通过键盘将数据输入到变量中] 9 运算符... …

c语言 大数开方,大数加法之C语言函数法(只有正数版)

由于某些原因&#xff0c;我于今天2017-4-19将我的博文搬到博客园了&#xff0c;以后我就在这里扎根了。 之前想过在博客写文章方便日后复习&#xff0c;但一直未能实现&#xff0c;所以&#xff0c;现在这篇是我个人人生中第一篇博客&#xff0c;所以写博客完全没经验&#xf…

开平方的快速算法(C代码)

目录&#xff1a; 一、牛顿迭代法 二、采用移位、加减法、判断和循环实现开平方 三、效率远高于牛顿迭代法开平方法 1、原理 2、实现代码 四、卡马克快速开平方算法(推荐) 1、C-Free模拟验证卡马克开平方 2、移植到实际的项目 3、卡马克快速开平方的由来 1&#xff0…

windows 区域截屏以及延迟截屏

提起在Windows&#xff0c; 我们都会用到截屏功能&#xff0c;今天论述一下window 10系统自带的截图应用Snipping Tool 打开Snipping Tool 找到任务栏下的放大镜图标&#xff0c;点击 在下方输入snipping&#xff0c;会在左侧找到截图软件Snipping Tool&#xff0c;点击可进入…

小米手机解决此区域不可截屏

小米手机解决此区域不可截屏 无意中暂停视频弹出消息&#xff0c;想试试可不可以截屏竟然可以截屏&#xff0c;但是视频一播放就截屏不了了&#xff0c;录屏也是&#xff0c;直接变黑或者是直接提示弹窗&#xff0c;嘻嘻嘻嘻小米bug还是有好处滴

浏览器网页截屏实用小技巧

浏览器开发者工具中自带的截屏太方便了&#xff01; 打开开发者工具&#xff0c;输入 ctrl shift P 快捷键&#xff0c;输入screenshot&#xff0c;出现了四个选项&#xff0c;分别是&#xff1a; 1.area screenshot - 区域截图 2.full size screenshot - 对浏览器所有内容…

如何利用计算机截屏快捷键,电脑怎么截图 电脑选区域截图怎么截 电脑截图快捷键是什么...

电脑怎么截图 按照操作上从易到难的顺序&#xff0c;给你推荐五种截屏方式 &#xff1a; 第一种&#xff1a;Ctrl PrScrn 使用这个组合键截屏&#xff0c;获得的是整个屏幕的图片; 第二种&#xff1a;Alt PrScrn 这个组合键截屏&#xff0c;获得的结果是 当前窗口的图片; 第三…

iOS 截屏指定区域

转自&#xff1a;链接&#xff1a;https://www.jianshu.com/p/39db0fa66c0e 指定截屏代码实现 全屏截图效果 全屏截图效果 指定区域截屏效果 指定区域截屏效果 这里先上代码&#xff0c;代码后面有相关方法的解释第一种方法代码下载 /**创建一个基于位图的上下文&#xff0…

windows如何截屏

截屏是我们平时工作或记录常用的操作&#xff0c;不过有人不知道怎么用系统截屏&#xff0c;今天&#xff0c;小编带来了系统的几种截屏&#xff0c;让我们来看看吧&#xff01; 一、快捷键截图 1. Win shift S&#xff1a;可以选择截图区域的大小&#xff0c;CtrlV粘贴在w…

Android 任意区域截屏

1、全屏截图 Android其实可以做到任意区域截屏&#xff0c;不过我们先来看看整个屏幕截图代码&#xff0c;相信大家很熟悉&#xff0c;代码如下 View decorView activity.getWindow().getDecorView(); decorView.setDrawingCacheEnabled(true); view.buildDrawingCache(); /…

snipaste 固定位置截屏

原文参考&#xff1a; snipaste怎么固定位置截图&#xff0c;如何统一大小截图 一、电脑点击【snipaste】&#xff0c;或者点击键盘的“F1”。 二、在图片&#xff0c;根据自己的需求&#xff0c;画出截图的位置和大小&#xff0c;比如&#xff1a;本篇是500*296。 三、点击右…

JavaScript实现浏览器特定区域截屏和下载功能

JavaScript实现浏览器特定区域截屏功能 需求介绍尝试一&#xff1a;使用Jtopo.js自带的保存图片方法&#xff08;不能对资源进行下载&#xff09;尝试二&#xff1a;对saveImageInfo进行改写&#xff08;功能能用&#xff0c;但是会因为跨域问题污染canvas&#xff09;&#xf…

Unity中的截图方法(包括全屏截图、区域截图、Camera截图和摄像头截图)

Unity中的截图方法&#xff08;包括全屏截图、区域截图、Camera截图和摄像头截图&#xff09; Application.CaptureScreenshotScreenCapture Texture2D.ReadPixels视口截图RenderTexture&#xff08;Camera截图&#xff09;WebCamTexture&#xff08;摄像头截图、照相&#xff…

实现区域截图功能

利用QQ或微信自带的截图功能实现区域截图。 在腾讯安装目录下找到PrScrn.dll&#xff0c;并将它放在需要的位置&#xff0c; 将D:/PrScrn.dll修改为你的目录。 如果在maya里面直接使用该代码 import os,subprocess from PySide2.QtWidgets import QApplication clipboard …

小米手机怎么截屏?小米手机区域截屏

小米手机怎么截屏&#xff1f;手机的截屏其实都是差不多的&#xff0c;基本上都是三指向下滑动而达到截屏效果的&#xff0c;但基本都是全屏截图。小米手机区域截屏怎么做&#xff1f;如果想要做到任意位置的那种区域块截屏的话&#xff0c;该怎么做&#xff1f;下面就来看看吧…