题目来源:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=488
素数环
时间限制:1000 ms | 内存限制:65535 KB
难度:2
描述
有一个整数n,把从1到n的数字无重复的排列成环,且使每相邻两个数(包括首尾)的和都为素数,称为素数环。
为了简便起见,我们规定每个素数环都从1开始。例如,下图就是6的一个素数环。
输入
有多组测试数据,每组输入一个n(0<n<20),n=0表示输入结束。
输出
每组第一行输出对应的Case序号,从1开始。
如果存在满足题意叙述的素数环,从小到大输出。
否则输出NoAnswer。
样例输入
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
-----------------------------------------------------
思路
深搜+剪枝。
递归深搜所有可能的排列,逐一判定。关键在于发现性质:如果n为奇数,则一定不能组成素数环(这是因为奇数个1~n中奇数多于偶数,必定存在两个不相同的奇数相邻,两个不相同的奇数之和一定是合数)。如果没有发现这条的话就会超时。
-----------------------------------------------------
代码
#include<iostream>
#include<cstring>
using namespace std;
int n;
int a[25] = {}; // 环上的数的序列
const bool prime[25] = {0,1,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0}; // 0~24质数表,是质数为1,不是质数为0
bool vis[25] = {}; // 是否在环上用过这个数
bool has_ans = false; // 一个Case是否有解判断标志
void dfs(int cnt) // 深搜输出可行解,cnt: 当前正在搜索环上第cnt个节点
{
int i;
if (cnt==n) // 如果环已经放满了
{
if (prime[a[n-1]+1]) // 判断最后一个数与第一个数之和是不是质数
{
has_ans = true; // 找到解
for (i=0; i<n; i++) // 直接输出
{
cout << a[i] << " ";
}
cout << endl;
}
return; // 环填完了,函数返回
}
if (cnt==0) // 填第一个数
{
a[cnt] = 1; // 第一个填的一定是1
vis[1] = 1;
dfs(1); // 递归深搜
}
else
{
for (i=1; i<=n; i++) // 遍历所有可能的n个数
{
if (!vis[i] && prime[a[cnt-1]+i]) // 如果环上没有用过这个数且它与前序节点之和为质数
{
a[cnt] = i; // 把这个数放到环上
vis[i] = 1; // 访问标记置1
dfs(cnt+1); // 递归深搜
vis[i] = 0; // 再把标记清除方便下一个分支的深搜
}
}
}
}
int main()
{
int t = 1; // Case ID
while (cin >> n)
{
if (n==0)
{
break;
}
memset(vis,0,sizeof(vis)); // 清空访问标记
cout << "Case " << (t++) << ":" << endl;
if (n==1) // n=1的情况特殊处理(此题把1也当做质数)
{
cout << 1 << endl;
continue;
}
if (n%2==1) // 如果是奇数,则环上不可能一奇一偶,必然有两不同奇数相邻,它们的和必定是合数
{
cout << "No Answer" << endl; // 排除奇数的情况是不超时的关键
continue;
}
has_ans = false; // 将找到可行解标志位置0
dfs(0); // 从环上第一个点开始深搜
if (!has_ans)
{
cout << "No Answer" << endl;
}
}
return 0;
}