http://acm.nyist.net/JudgeOnline/problem.php?pid=488
描述
有一个整数n,把从1到n的数字无重复的排列成环,且使每相邻两个数(包括首尾)的和都为素数,称为素数环。
为了简便起见,我们规定每个素数环都从1开始。例如,下图就是6的一个素数环。
-
输入
- 有多组测试数据,每组输入一个n(0<n<20),n=0表示输入结束。 输出
-
每组第一行输出对应的Case序号,从1开始。
如果存在满足题意叙述的素数环,从小到大输出。
否则输出No Answer。
样例输入
-
6 8 3 0
样例输出
-
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 Case 3: No Answer
来源
- hdu改编 上传者
- 丁国强
解法1:生成测试法(TLE)
#define RUN
#ifdef RUN
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <vector>
#include <list>
#include <cctype>
#include <algorithm>
#include <utility>
#include <math.h>
#include <ctime>
using namespace std;
int is_prime(int x) {
for(int i = 2; i*i <= x; i++)
if(x % i == 0) return 0;
return 1;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("nyist488.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
int n, A[50], isp[50];
//scanf("%d", &n);
int cases = 1;
while(cin>>n && n!=0){
bool noAnswer = true;
cout << "Case " << cases << ":" << endl;
cases++;
//生成素数表,加快后面的判断
//判断第i个数是否是素数,如果isp[i]返回1则是,返回0则否
for(int i = 2; i <= n*2; i++) isp[i] = is_prime(i);
//A[0]:1
//A[1]:2
//A[2]:3
//...
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) {
noAnswer = false;
for(int i = 0; i < n; i++) printf("%d ", A[i]);
printf("\n");
}
}while(next_permutation(A+1, A+n));
if(noAnswer){
cout << "No Answer" << endl;
}
}
return 0;
}
#endif
解法2:回溯法+剪枝
同时注意自环的判断
#define RUN
#ifdef RUN
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <vector>
#include <list>
#include <cctype>
#include <algorithm>
#include <utility>
#include <math.h>
#include <ctime>
using namespace std;
bool noAnswer;
int cases = 1;
#define MAXN 10000
int is_prime(int x) {
for(int i = 2; i*i <= x; i++)
if(x % i == 0) return 0;
return 1;
}
int n, A[MAXN], // A数组用于保存结果 A[x]=y 第x个位置放了大小为y的数
isp[MAXN], // isp数组用于判断是否素数 isp[x]=y 大小为x的数,其素数属性为y
vis[MAXN]; // vis数组用于标示第i个数是否已经被使用过了 vis[x]=y,大小为x的数,已使用性为y
void dfs(int cur) {
//递归边界,同时测试第一个数和最后一个数
if(cur == n && isp[A[0]+A[n-1]]) {
noAnswer = false;
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; //标志把数i放在cur的位置上
vis[i] = 1; //标志i已经用了
dfs(cur+1); //寻找下一个位置应该放的数
vis[i] = 0; //还原全局变量,清除标志
}
}
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("nyist488.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
//生成素数表,加快后面的判断
//判断第i个数是否是素数,如果isp[i]返回1则是,返回0则否
memset(isp, 0, sizeof(isp));
//isp[2] = isp[3] = isp[5] = isp[7] = isp[11] = isp[13] = isp[17] = isp[19] = isp[23] = isp[29] = isp[31] = isp[37] = 1;
for(int i = 2; i <= 20*2; i++){
isp[i] = is_prime(i);
}
A[0] = 1;
while(scanf("%d",&n) && n!=0){
noAnswer = true;
cout << "Case " << cases << ":" << endl;
cases++;
memset(vis, 0, sizeof(vis));
//自环的情况
if(n==1) { printf("%d\n",A[0]); continue;}
//重要的剪枝!!!
//如果n为奇数,存在不等于2的偶数,不为素数,可以直接排除
// 奇+偶=奇
// 奇+奇=偶
// 偶+偶=偶
if(n%2 != 0) { printf("No Answer\n"); continue;}
dfs(1);
if(noAnswer){
cout << "No Answer" << endl;
}
}
return 0;
}
#endif