问题:输入正整数n,把整数1,2,3,…,n组成一个环,使得相邻两个整数之和均为素数。输出时从整数1开始逆时针排列。n<=16;
样例输入:
6
样例输出:
1 4 3 2 5 6
1 6 5 2 3 4
方法一:直接枚举法
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int isp[200],A[200];
int is_prime(int n)
{
int flag, i;
flag = 1;
for (i = 2; i <=sqrt(n);i++)
if (n%i == 0){flag = 0; break;}
return flag;
}
int main()
{
int n;
cin >> n;
for (int i = 2; i <= 20; i++)isp[i] = is_prime(i);
for (int i = 0; i < n; i++)A[i] = i + 1;
do{
int ok = 1;
for (int i = 0; i < n; i++)if (!isp[A[i] + A[(i + 1) % n]]){ ok = 0; break; }
if (ok){
for (int i = 0; i < n; i++)printf("%d ", A[i]);
printf("\n");
}
} while (next_permutation(A + 1, A + n));
system("pause");
return 0;
}
程序的基本思路是保留1的位置不变,其他数字不断生成全排列,然后判断该序列是否符合要求,如果符合就输出。然而,该方法虽然比较直观,但效率比较差,当n为12时就已经很慢了。
方法2:回溯法
程序如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int isp[200],A[200],vis[200];
int n;
int is_prime(int n)
{
int flag, i;
flag = 1;
for (i = 2; i <=sqrt(n);i++)
if (n%i == 0){flag = 0; break;}
return flag;
}
void dfs(int cur){
if (cur == n&&isp[A[0] + A[n - 1]]){
for (int i = 0; i < n; i++)printf("%d ", A[i]);
printf("\n");
}
else for(int i =2; i <= n;i++)
if (!vis[i] && isp[i + A[cur - 1]]){
A[cur] = i;
vis[i] = 1;
dfs(cur+1);
vis[i] = 0;
}
}
int main()
{
cin >> n;
for (int i = 2; i <= 20; i++)isp[i] = is_prime(i);
A[0] = 1;
dfs(1);
system("pause");
return 0;
}
由于前面已经画了类似流程图,这里不再画。简单来说,每次递归的过程就是在一个解答树的分支中不断尝试剩下的元素,如果能符合要求,就放到素数环里面。素数环与之前唯一不同之处在于A[0]是确定为1的,只需确定1除外的其他元素的位置。