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

article/2025/9/15 9:48:42

八皇后问题

1.递归回溯法

B站懒猫老师讲的(我在这里学的)
八皇后问题的递归回溯算法思路:从第一行开始当某一行皇后位置不与前面所有皇后位置冲突那么记录该行皇后位置并调用递归函数进入下一行,摆放下一个皇后,逐个位置摆放,若该行所有位置都被其他皇后占领,那么就回溯到上一行重新摆放上一行皇后直至所有皇后都不冲突那么记录一次方法然后回溯寻找其他摆放方法。

冲突算法思路:一个8*8的棋盘每一个位置若用其行号加上其列号我们可以得到下图,由图可知在同一条上对角线的数值都相同,因此我们可以利用该规律设计判断上对角线是否冲突的函数,类似的,下对角线则是由列号减去行号可得在同一条下对角线的数值相等。为了方便后面的程序中我把下对角线的数值都加7为正数(实际效果不变)
行号+列号
行号-列号

#include<stdio.h>
int place[8]={0};//皇后位置
bool flag[8]={1,1,1,1,1,1,1,1};//定义列
bool d1[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};/*定义上对角线(共有15个对角线,
因此定义一个长度为15的bool型数组,初值为1代表该对角线没有被皇后占领,
若被皇后占领则赋值为0*/
bool d2[15]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} ;//定义下对角线
int number=0;//记录输出次数
void print()//定义输出函数
{int col,i,j;
number++;//每调用一次输出函数number自加一次,记录摆放方法个数printf("No.%2d\n",number);int table[8][8]={0};//设置一个8*8的棋盘for (i=0;i<8;i++){table[i][place[i]]=1;//将每一行皇后所在位置赋值为1}for (i=0;i<8;i++){for (j=0;j<8;j++){printf("%d|",table[i][j]);}printf("\n");}
}
int queen(int n )//定义递归回溯函数
{int col;for (col=0;col<8;col++){if (flag[col]&&d1[n-col+7]&&d2[n+col])//判断皇后是否冲突{place[n]=col;//放置皇后flag[col]=false;d1[n-col+7]=false;d2[n+col]=false;//将该皇后所在的行、列、对角线设置为被占领if(n<7)	{queen(n+1);}//当行数小于7时;递归调用下一行else{print();}//调用输出函数flag[col]=true;//回溯d1[n-col+7]=true;d2[n+col]=true;}}return number;
}
int main()
{
number=queen(0);//从第0行开始执行
printf("共有%d种摆放方法",number);//输出方法的个数return 0;}

利用递归回溯的方法来解决八皇后问题的程序步骤较少且可读性强,代码量少,不用尝试所有错误的摆放方法,遇到错误就回溯,效率较高。但递归算法占用空间更大,且每调用一次函数都要在内存栈中分配一定的空间,而栈的空间是有限的,若递归调用的层次太多就容易造成栈的溢出。

2 回溯法(非递归)

位置冲突算法:建立一个一维数组a[9](a[0]不用),a[1]~a[9]八个数分别代表棋盘的一行,其中的数值代表该行皇后所在的列数,若任意两个数的数值相等则表明在同一列(冲突),若任意两个数所在的行数差值的绝对值等于列数的差值的绝对值(在同一对角线上冲突)

#include <stdio.h>
#include <math.h>bool Chongtu( int a[], int n) {int i = 0 , j = 0 ;for (i = 2 ; i <= n; ++i)          
for (j = 1 ; j <= i- 1 ; ++j)              
if ((a[i] == a[j]) || (abs(a[i]-a[j]) ==abs(i-j)))//1:在一列;2:在对角线上。每一行都要与前面所有行进行判断是否冲突return false ;   //冲突return true ;} //不冲突
//八皇后问题:迭代法(回溯)
void Queens8(){int n = 8 ;        //8个皇后int count = 0 ;//记录当前第几情况int a[ 9 ] = { 0 };   //存放皇后位置,(第0行0列不用,便于直观看出皇后位置)int i = 0 ,k = 1 ;  //初始化k为第一行a[ 1 ] = 0 ;     //初始化a[1] = 0while (k > 0 )    {a[k] += 1 ;    //a[k]位置,摆放一个皇后while ((a[k] <= n) && (!Chongtu(a,k))) //如果a[k](即皇后摆放位置)没有到k行最后,且摆放冲突。a[k] += 1 ; //将皇后向后移一位直至不冲突或a[k]>n超出范围则结束循环if (a[k] <= n) //皇后摆放位置没有到达该行最后一个位置{if (k == n) //8行皇后全部摆放完毕
{
printf( "第%d种情况:\n" ,++count);for (i = 1 ; i <= n; ++i) //打印该种摆放方法情况{for (int j=1;j<=8;j++)		
{    	
if (j!=a[i])		
{   
printf("0|");}else{printf("1|");}}
printf("\n");}
printf("\n");}else      //皇后还未摆放完毕{k += 1 ;    //继续摆放下一行a[k] = 0 ;  //此行初始化a[k] = 0;表示第k行,从第一行开始摆放皇后}}else  //回溯:当a[k]>8进入else,表示在第k列中没有找到合适的摆放位置k -= 1 ; //回溯到k-1步:k表示第几个皇后,a[k]表示第k个皇后摆放的位置}return ;
}
//主函数
int main()
{Queens8();return 0 ;
}

回溯法实质上是利用迭代实现递归回溯的方法,该方法运行效率高,但是代码量更大也更加复杂。

3 枚举法(1)

枚举法即列举出所有摆放方法(无论是否冲突),当八个皇后摆放完毕后再判断该方法是否合理。
冲突算法与回溯(非递归)法相同

#include <stdio.h>
#include <math.h>bool Chongtu( int a[], int n) //判断皇后是否冲突的函数
{int i = 0 , j = 0 ;for (i = 2 ; i <= n; i++) //i:皇后的位置for (j = 1 ; j <= i- 1; j++) //j:皇后的位置if ((a[i]== a[j]) || (abs(a[i]-a[j]) ==abs(i-j))) //1:在一列;2:在对角线上。每一行都要与前面所有行进行判断是否冲突{return false ;} //冲突return true ;}//不冲突
void Queens8()//枚举
{int a[ 9 ] = { 0 }; //用于记录皇后位置:(第0行0列不用,便于直观看出皇后位置)。int i = 0 ,count = 0 ;  
//枚举出八个皇后摆放位置的所有情况
//利用八重循环列举出所有方法并逐一验证for (a[ 1 ] = 1 ; a[ 1 ] <= 8 ; ++a[ 1 ])for (a[ 2 ] = 1 ; a[ 2 ] <= 8 ; ++a[ 2 ])for (a[ 3 ] = 1 ; a[ 3 ] <= 8 ; ++a[ 3 ])for (a[ 4 ] = 1 ; a[ 4 ] <= 8 ; ++a[ 4 ])for (a[ 5 ] = 1 ; a[ 5 ] <= 8 ; ++a[ 5 ])for (a[ 6 ] = 1 ; a[ 6 ] <= 8 ; ++a[ 6 ])for (a[ 7 ] = 1 ; a[ 7 ] <= 8 ; ++a[ 7 ])for (a[ 8 ] = 1 ; a[ 8 ] <= 8 ; ++a[ 8 ])
{	
if (!Chongtu(a, 8 )) /*调用判断该种摆放方法是否冲突的函数,
若冲突则枚举下一种方法,若不冲突则记录此种摆放方式	*/{continue ;}else {							
printf( "第%d情况:\n" ,++count);for (i = 1;i<= 8 ;i++){for (int j=1;j<=8;j++){if (j!=a[i]){printf("0|");}else{printf("1|");}}printf("\n");}//打印该种摆放方法
printf( "\n" );}}
}
int main()//主函数
{Queens8();return 0 ;
}

枚举法思路和代码都简单易懂,但程序的计算量大,方法较基础。

4 枚举法(2)

枚举法(2)的冲突算法及思路与枚举法(1)类似,该方法虽然简化了部分代码,但是时间复杂度更大。(大家可以对照着枚举法1理解我就没加注释了)。

#include <stdio.h>
#include <math.h>
int count = 0;//记录方法次数//输出函数
void showAnswer(int *queen){int i,j;
count++;
printf("第%d种解法:\n",count);
for(i = 0; i < 8; ++i)
{for(j = 0; j < 8; ++j){if(queen[i] == j)printf("1|");elseprintf("0|");}printf("\n");
}printf("\n");}
//判断是否皇后位置是否冲突算法
void chongtu(int *queen){int i,j,flag=1;for(i = 0; i < 8; ++i){for(j = 0; j < 8; ++j){if(i != j){if(queen[i] == queen[j]){flag = 0;}else if(abs(queen[i] - queen[j]) == abs(i-j))flag = 0;}}}if(flag == 1){
showAnswer(queen);//若该方法可行则调用输出函数
}}
//枚举出所有摆放方法(相较于枚举法(1)简化了八重循环)
void set_queen(int *queen)
{int i,flag;while(queen[8] != 1){++queen[0];for(i = 0;i<8; ++i){if(queen[i]==8)            {   queen[i]=0;{ ++queen[i+1];}}}flag =1;chongtu(queen);//摆放后调用位置冲突算法}
}
int main()
{int queen[9] = {0};set_queen(queen);return 0;
}

目前我只学了这四种方法(在原作者方法上进行了部分更改也增加了一下注释和个人的理解),这四种方法比较好理解,希望能帮助到大家。


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

相关文章

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

问题描述 在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;下面就来看看吧…

浏览器截图方法(长截图、node截图、指定区域截图)

1.打开需要截屏的页面&#xff0c;按键盘上的F2&#xff08;或者CtrlShiftI&#xff09;打开浏览器控制台 2.CtrlshiftP进入搜索框&#xff0c;输入“screen”: 这里有四种截图模式&#xff0c;点击需要的截图方式即可截取图片。

Windows关闭指定端口命令

假设要关闭端口号为3003&#xff0c;使用下面的命令&#xff0c;查出此端口号对应的PID netstat -ano|findstr 3003 上图红框内的 22876 就是3003端口对应的PID&#xff0c;再使用下面的命令就可以关闭这个端口了 taskkill /PID 22876 /F