问题:
输入正整数n,把整数1,2,3,4…..n组成一个环,使得相邻的两个整数之和均为素数。输出是,从整数1开始逆时针排列,n<=16;
样例输入:
6
样例输出:
1 4 3 2 5 6
1 6 5 2 3 4
(1)直接枚举
首先我们先不用回溯法来做,
使用直接枚举全部排列,然后得出解:
#include<iostream>
#include<algorithm>
using namespace std;
int A[100], isp[100], n;//isp是素数表,用于存放素数
int is_prime(int x){
for(int i = 2; i < x; i++){
if(x % i == 0) return 0;
}
return 1;
}
void printPrime(){
//生成第一个排列,顺序排列, A[1] = 1 A[2] = 2
for(int i = 1; i <= n; i++){
A[i] = i;
}
do{
bool ok = true;
for(int i = 1; i < n; i++){
int index = A[i]+A[i+1];
if(!isp[index]){
ok = false;
break;
}
}
//第一个和最后一个数的和
if(!isp[A[1] + A[n]]){
ok = false;
}
if(ok){
cout << "当前结果";
for(int i = 1; i <= n; i++){
cout << A[i] << " ";
}
cout << endl;
}
} while(next_permutation(A+2, A+n+1));//第一个一定为1不变,只更新第2个到最后1个的排列
}
int main(){
cin >> n;
//记录1~n*2的值是否为素数
for(int i = 2; i <= n*2; i++){
isp[i] = is_prime(i);
}
printPrime();
}
但是直接枚举的话,到n=12已经很慢了,n=16更是得不出结果
(2)使用回溯法
使用回溯法的解答树:
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int A[100], isp[100], vis[100], n;//isp是素数表,用于存放素数
int is_prime(int x){
for(int i = 2; i < x; i++){
if(x % i == 0) return 0;
}
return 1;
}
void dfs(int cur){
if(cur == n && isp[A[0] + A[n-1]]){//还要测试环的头尾之和是否为素数
for(int i = 0; i < n; i++){
cout << A[i] << " ";
}
cout << endl;
}else{
for(int i = 2; i <= n; i++){//第一个位置值为1,循环测试1和 2-6某个数的和是否为素数
/*isp[i+A[cur-1]]是剪枝函数,
假如A[cur-1] = 1, i=3,此时之和不为素数,则把该支点剪掉,不往该子树递归
相比于直接枚举,回溯法思想便体现在此
*/
if(!vis[i] && isp[i+A[cur-1]]){
A[cur] = i;
vis[i] = 1;//当前i的值已经放在了cur的位置,当其它层次的递归遍历到值i时,避免重复
dfs(cur+1);
//要复位
vis[i] = 0;
A[cur] = 0;
}
}
}
}
int main(){
cin >> n;
for(int i = 2; i <= n*2; i++){
isp[i] = is_prime(i);
}
A[0] = 1;
dfs(1);
}