素数环 Prime Ring Problem
题面翻译
输入正整数 n n n,把整数 1 , 2 , … , n 1,2,\dots ,n 1,2,…,n 组成一个环,使得相邻两个整数之和均为素数。输出时,从整数 1 1 1 开始逆时针排列。同一个环恰好输出一次。 n ≤ 16 n\leq 16 n≤16,保证一定有解。
多组数据,读入到 EOF
结束。
第
i
i
i 组数据输出前加上一行 Case i:
相邻两组数据中间加上一个空行。
题目描述
输入格式
输出格式
样例 #1
样例输入 #1
6
8
样例输出 #1
Case 1:
1 4 3 2 5 6
1 6 5 2 3 4
Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2
【问题分析】
需要在n个全排列里挑选出满足条件的方案。很显然,没有公式或者规律来解此题,只能通过逐位尝试的方式来进行,因此可用回溯法解决。从第1个位置开始尝试,每个位置有n中选择,需保证填写的数在之前位置没有被使用过,且和上一个数之和为素数。当填写第n个位置的时候需要额外保证第n个数和第1个数之和为素数。完整代码如下:
#include<bits/stdc++.h>
using namespace std;
bool vis[20];//vis[i]=1,表示数字i已经被占用,此后步骤不能填写数字i,以防数字被重复使用
int a[20],n;
bool isPrime(int x){
if(x<2)return 0;
for(int i=2;i<=sqrt(x);i++){
if(x%i==0)return 0;
}
return 1;
}
void dfs(int k){
if(k==n+1){
if(isPrime(a[n]+a[1])){
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
return;
}
for(int i=1;i<=n;i++){
if(vis[i]==true)continue;
if(!isPrime(i+a[k-1]))continue;
a[k]=i;
vis[i]=true;
dfs(k+1);
a[k]=0;
vis[i]=false;
}
}
int main(){
int cnt=0;
while(cin>>n){
a[1]=1;
vis[1]=1;
cnt++;
cout<<"Case "<<cnt<<":"<<endl;
dfs(2);
cout<<endl;
}
return 0;
}
既然本题需要枚举排列,还可以使用STL中的next_premutation来自动枚举排列。每次枚举出一个排列,然后验证是否满足要求即可。完整的代码如下:
#include<bits/stdc++.h>
using namespace std;
int a[20],n;
bool isPrime(int x){
if(x<2)return 0;
for(int i=2;i<=sqrt(x);i++){
if(x%i==0)return 0;
}
return 1;
}
int main(){
int cnt=0;
while(cin>>n){
cout<<"Case "<<++cnt<<":"<<endl;
for(int i=1;i<=n;i++){
a[i]=i; }
do{
if(a[1]!=1)break;//题目规定一个数必须为1
bool flag=1;
for(int i=2;i<=n;i++){
if(!isPrime(a[i]+a[i-1])){
flag=0;
break;
}
}
if(!isPrime(a[1]+a[n])){
flag=0;
}
if(flag==1){
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
}while(next_permutation(a+1,a+n+1));
cout<<endl;
}
return 0;
}