《算法竞赛入门经典》-【第七章:暴力求解法】-7.4:回溯法

一、问题

把正整数1,2,3...n组成一个环,使得相邻两个整数之和为素数,输出时从整数1开始逆时针排列。n不大于16。

 

二、思路

最直接的方式是列出所有的素数排列,然后逐个判断是否满足要求,这种方式很简单好理解,但是问题在于所有的素数列量太大,速度会很慢。从另一个角度来看,7.2,7.3,7.4本质上都是广义上的搜索问题,而且都是可以分步骤用递归解决的。本质上,这种解决方法都是在解答树上通过深度优先搜索出符合要求的节点。对于7.3,所有的节点都包含符合要求的答案。对于7.2和7.4,这些包含答案的节点都是叶子节点,不同的是7.2中的每个叶子节点都是答案,而7.4由于题目要求的严格,很过叶子节点都不满足要求因此不是答案。更进一步的,我们很自然的可以想到在搜索的过程中,有时候实际上不用走到叶子节点就知道“此路不通”了,例如给定5个数,如果选择了第一个数为1,且第二个数为3的组合,不用看第三个数就知道肯定无法构成素数环了,因此也不用继续增加深度了,而是调转头来(backtrace,即回溯)考虑第二个数的其他可能性。


三、代码

1. 伪代码

curr:当前步数,初始化为1
A:当前已经存放了curr个数字的数组,初始化A[0]=1
不变逻辑:
a. 如果curr==n,判断最后一个数和第一个数的和是否为素数,是的话打印A,否则返回
b. 对于2~n中的每个数字,如果没有出现在A中,并且和A[curr-1]的和为素数,则放入A[curr],curr++,进入下一步。
递归边界:curr == n

2.代码
public class Backtrace {

	private static Integer N = 16; 
	
	public static void main(String[] args) {
		Integer[] output = new Integer[N];
		output[0] = 1;
		primeNumberCircle(output,1);
	}
	
	private static void primeNumberCircle(Integer[] A, Integer curr){
		if(curr==N){
			//检查第一个数和最后一个数的和是否为素数
			if(isPrime(A[0]+A[N-1])){
			for(int i=0; i



四、总结
1. 递归一定有的:一个记录中间结果的数组A,一个记录层次深度的变量curr
2. 回溯法的关键是寻找合适的剪枝条件

你可能感兴趣的:(算法)