当前位置: 首页 > news >正文

菜鸟记录:c语言实现洛谷P1219 ———八皇后

非常经典的回溯问题,运用的数学知识也很巧妙。

题目:

一个如下的 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 2 4 6 1 3 5 来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 6

列号 2 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 个解。最后一行是解的总个数。

输入格式

一行一个正整数 n,表示棋盘是 n×n 大小的。

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

输入输出样例

输入 :

 

6

  

输出 :

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

 

题目分析:

  初看题目会非常容易想到通过穷举的方式,在合适的位置对大棋盘这个二维数组标记行、列、斜线,通过递归进行行的变化,并用一个一维数组存储所需要输出的位置。但在编写的过程中可以发现,标记过程是冗余的,例如坐标(0,0)会标记包括(0,2)在内的行,(1,0)在内的列,(1,1)、(2,2)在内的斜线,而显然,作为第二行可能的元素(1,2)也会重复标记这几个点,这不仅使算法显得冗余,也增大了操作的出错率。这时就有读者要问了,那有没有一种办法可以只标记一次呢?有的,兄弟,有的。这里就要用到我们敏锐的观察力了。

  我们可以注意到,由于一行一列只会有这一个元素,而无论是不是穷举,我们都会通过行变换来推动循环,所以行值完全不用标记,默认有一个后立马进行下一次循环,可以把列标记情况和对角线标记情况单独用数组存储,通过一定的函数关系判断当前递归元素是否被标记,而这个重点就在于对角线的标记。从主对角线出发,我们可以看到(0,0)、(1,1)...(n,n),列减去行都为0,(1,0)、(2,1)...(n+1,n),列减去行都为1,(0,1)、(1,2)...(n,n+1),列减去行都为-1,以此类推,我们可以得到以下这张图:

 

image

 

  如此一来,我们只要用一个一维数组就可以存储主对角线上的标记情况这个数组下标从0到2n-1,标记着行列差值从-n+1到n-1。同样的,我们还要用一个数组来保存反对角线,可以看到(0、0)列加上行都为0,(1,0)、(1,1),列加上行都为1 ...(n-1,n-1),列加上行都为2n-2,以此类推,我们可以得到以下这张图:

 

image图片来自经典算法-----八皇后问题(回溯算法)-CSDN博客

 

  由此,我们可以得到,对于主对角线,我们有 

f(x)=row-col+n-1      加上n-1是为了使结果作为下标不会成为负数

  反对角线有

g(x)=row+col

  其中row代表行值,col代表列值,n代表棋盘大小。

  接下来就是代码问题,首先,我们要明白回溯的一个的核心就是“恢复”,代码形式类似:

HuiShuo(){代码A;代码B;代码C;HuiShuo();代码a;代码b;代码c;。。。
}

  其中代码a、b、c是代码A、B、C的”返回“,如A为i++,则a为i--。以此达到“恢复”的作用。对于本题,则A、B、C很明显为对列、主对角线和反对角线的标记,a、b、c则为对其标记的“恢复”。我们通过递归实现行的变化,通过循环语句实现对列的变化,在遍历整个棋盘时,通过数组queen来记录棋子放置位置,下标为行坐标,内容为列坐标,如queen[2]=5,则该棋子在(2,5)的位置,每便利到最后一行并符合条件时,则视为一个方式,sum++,超过四条只计数不输出。

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 
 4 int n;    //棋盘大小
 5 int sum;    //方式总数
 6 int* queen;    //放置棋子
 7 int* col;   //列标记
 8 int* dig1;   //主对角线标记
 9 int* dig2;   //反对角线标记
10 
11 void Queen(int row) {    //row为行
12     if (row == n) {
13         sum++;
14         if (sum < 4) {
15             for (int i = 0; i < n; i++) {
16                 printf("%d ", queen[i]+1);
17             }
18             printf("\n");
19         }
20     }    //递归到最后一行时说明存在放置方式,输出结果,超过四条不输出
21     for (int i = 0; i < n; i++) {
22         if (!col[i] && !dig1[row - i + n - 1] && !dig2[row + i]) {    //列、主对角线、反对角线均未标记,则将该位置放置棋子,并递归
23             queen[row] = i;
24             col[i] = 1;                    
25             dig1[row - i + n - 1] = 1;
26             dig2[row + i] = 1;
27 
28             Queen(++row);
29             row--;
30             col[i] = 0;
31             dig1[row - i + n - 1] = 0;
32             dig2[row + i] = 0;
33         }
34 
35     }
36 }
37 
38 int main() {
39     scanf("%d", &n);
40     queen = (int*)malloc(n * sizeof(int));
41     col = (int*)calloc(n ,sizeof(int));
42     dig1 = (int*)calloc(2*n , sizeof(int));
43     dig2 = (int*)calloc(2*n , sizeof(int));    //初始化
44     Queen(0);
45     printf("%d", sum);
46     return 0;
47 }    

 

 运行通过!

image

 

 

 

 

http://www.hskmm.com/?act=detail&tid=18334

相关文章:

  • 当危机爆发时,所有网络安全都是本地的
  • crc校验原理是什么?
  • CF1385D a-Good String
  • 9月23日(日记里有)
  • 9月25日(日记里有)
  • Git 提交代码前,一定要做的两件事
  • 本地调试接口时遇到的跨域问题,十分钟解决
  • 用 Excel 快速处理接口返回的 JSON 数据
  • 调度的基本概念
  • Overleaf项目文件同步工具: olsync
  • CF1995D Cases
  • 日志| 编辑距离 | 最长有效括号 |
  • day5
  • 《etcd库——键值存储系统》 - 教程
  • 有一个函数只会返回0和1,且返回0和返回1的概率不等。要求只能通过这个函数生成一个等概率返回0和1的函数
  • AI智能体开发实战:17种核心架构模式详解与Python代码实现
  • 代码随想录算法训练营第十天 | 232. 用栈实现队列、225. 用队列实现栈、20. 有效的括号、删除字符串中的所有相邻重复项
  • 2025.9.26总结 - A
  • MySQL性能优化
  • 关于“悬荡悟空”决策机制的简要技术说明
  • 最小二乘问题详解1:线性最小二乘
  • 9月26日
  • 工程监理行业多模态视觉​​​​​​​大模型系统,打造工地行业全场景的监理智能生态
  • 完整教程:【鸿蒙心迹】摸蓝图,打地基
  • 正则表达式
  • LuatOS Air780EPM 实现 HTTP 通信:从原理到代码实践
  • 搜维尔科技:Senseglove Nova 2触觉手套:虚拟训练、VR/AR模拟和研究中的触觉反馈
  • 深入解析:盟接之桥EDI软件:中国制造全球化进程中的连接挑战与路径探索
  • 【STM32H7】基于CubeMX从零开始搭建的HAL库工程模板(包含串口重定向和DSP库)
  • 在Windows架构中安装Miniforge及python环境变量配置