素数环—回溯法
问题介绍
给定数字 n ,排列从 1 到 n 的数字成环,使得相邻的俩数字之和为素数(质数)
问题分析
本文引入此问题是为了讲解下回溯法。介绍下回溯法:
大家应该知道迷宫,在走迷宫的时候选定一条道路,走不通回来继续走,走不通回来继续走… 回溯法类似,回溯嘛,就是走不对回来换条路呗,直到找到合适的问题解决方案。
再到本问题来,分析下数据结构,环嘛,就是线性的,所以用个一维数组存放环即可,在此定义环数组为 home,大小为 n 。对于一个环,要有一个起点,但是起点是哪个数字无所谓,在此初始化数组第一个元素
为 1 。另外需要另一个数组保存给定的数字的集合,所以定义该数组为 arr ,大小
为 n。上面讲到,已经初始化化环的第一个元素,所以就从该元素开始。逻辑部分如下:
取得环的当前最后一个元素 lastNumber,遍历数组 arr ,若遍历过程中当前数字
不存在于 home 数组中 且 当前数字与 lastNumber 和为质数,则将该数字添加
到环的末尾,准确说是指定下标的位置。到环的长度等于 n 时,便是一种解决方案。
代码实例(C语言)
#include<stdio.h>
#include<stdlib.h>
#include<Math.h>
int n = 16; // 示例数值
int arr[16]; // 用于存放所有的数值
int home[16]; // 用于存放环
int total = 0; // 方法计数器
void initArr(){ // 填充 arr[]
for (int i = 0; i < n; i++)
{
arr[i] = i + 1;
}
}
void output(){ // 输出 home[]
for (int i = 0; i < n; i++)
{
printf("%d ", home[i]);
}
printf("\n");
}
/* 判断当前数字是否存在于 home数组中 */
/*
@param number 被检测的数字
@param index 当前环的长度,也是环当前需放置数字的下标
*/
bool isExist(int number, int index){
for (int i = 0; i < index; i++)
{
if (number == home[i]) return true;
}
return false;
}
/* 判断数字是否为质数 ( 需引入 #include<Math.h> ) */
/* @param number 所检测数字 */
bool isPrime(int number){
int sqrtNumber = sqrt(number);
for (int i = 2; i <= sqrtNumber; i++)
{
if (number % i == 0) return false;
}
return true;
}
/* 逻辑主函数(递归) */
/* @param inedx 环当前需要放置元素的下标 */
bool prime(int index){
/*
@条件判定 if 当前下标等于 n 并且当前环的第一个数字和最后一个数字和为素数( 环嘛 )
{
输出当前的环,方案+1
}
@条件判定 else
{
获取当前环的最后一个数字;
遍历数组 arr
若遍历中 该数字不存在于 home数组中 并且 与环中最后一个数字和为素数
将该数字添加到环中,下标是指定的,切记,不是 push操作
查找下一个
}
*/
if (index == n && isPrime(home[0] + home[index - 1])){
output();
total ++;
}else {
int lastNumber = home[index - 1];
for (int i = 0; i < n; i++)
{
if (!isExist(arr[i], index) && isPrime(lastNumber + arr[i]))
{
home[index] = arr[i];
prime(index + 1);
}
}
}
}
int main(){
initArr(); //初始 arr
home[0] = 1; // 默认环第一个元素为 0
prime(1); // 开始逻辑主函数 传入 下标 1
printf("方法:%d", total);
}
代码分析
其他不多说,分析下逻辑主函数 bool prime()。逻辑都在注释中,但是一定要注意添加新元素到环中,一定是指定的下标。这个也是回溯法的关键。
总结
回溯法其实是有模板的,而且非常结构非常典型,基本上可以按照模板解决相应的问题。回溯法最重要的地方就是试探当前状态 未来的所有情况,对于未来的某个情况也适用,如此递归下去,在递归开头设置满足递归结束的条件即可,唯一需要记住的是对于当前情况的保存一定是固定下标这样的。