来源:入门经典
生成-测试法:
#include<cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;
#define ll long long
int isp[1000],A[1000];
int is_prime(int x)
{
int i;
for(i=2;i<=sqrt(x);i++)
{
if(x%i==0) break;
}
if(i>sqrt(x))
return 1;
else
return 0;
}
int main()
{
int n=6;
for(int i=2;i<=n*2;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)); //1的位置不变//next_permutation()是求下一个排列
return 0;
}
输出:
当n=12时,用时
回溯法:
#include<cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;
#define ll long long
int isp[1000],A[1000];
int vis[1000];
int n;
int is_prime(int x)
{
int i;
for(i=2;i<=sqrt(x);i++)
{
if(x%i==0) break;
}
if(i>sqrt(x))
return 1;
else
return 0;
}
//回溯法
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++) //尝试放置每个数i
if(!vis[i]&&isp[i+A[cur-1]]) //如果i没用用过,并且与前一个数之和为素数
{
A[cur]=i;
vis[i]=1; //设置使用标志
dfs(cur+1);
vis[i]=0;//清除标志
}
}
//回溯法比生成-测试法快了好多,即使n=18速度也不错。顺便说一句,把上面的函数名取为dfs并不是巧合--从解答树的角度
//讲,回溯法正是按照深度优先的顺序在遍历解答树
int main()
{
n=12;
memset(vis,0,sizeof(vis));
for(int i=2;i<=n*2;i++) isp[i]=is_prime(i);
A[0]=1;
dfs(1);
return 0;
}
而n=12时,用时(快了一些)
而当n=16是两者差别就很大了,虽难都有点慢