题目:
http://acm.nyist.net/JudgeOnline/problem.php?pid=488
思路:
“全排列”思想。
以“6”为例,
1 4 3 2 5 6 1 6 5 2 3 4
1.上面两组可以构成素数环,所谓“环”, 即循环,只取出全排列中以1开头的那部分就可以,换句话说,对2-6进行全排列就可以了。
2.“素数”,每两个的和必须是素数,注意素数的判定(前面整理过了);
3.“2-6”排列中的第一个数,要和1相加,判断一下和是否是素数;
4.“环”, 首尾要连接起来,也要判定是否构成素数;
实现:
使用标记数组B,记录当前遍历到的数是否被选过(少用了一个判断,即else里面只有一个循环,时间明显提高);
改进的程序中,else里面只有一个循环,比之前提高了效率;
只有一个1的时候,也构成一个素数环;
当输入的数是除1之外的素数的时候,不用进入递归去判断,直接输出No answer就可以了, 同时也提高了时间效率(当初也就是改了这里,改正了超时的错误);
#include<stdio.h>
#include<string.h>
void Primes();
void PrimeCirculer(int n, int cur);
bool number[40] = {0}; //素数标记数组, 0 为素数,1为非素数;
bool B[200] = {0, 1}; //标记数组;
int A[20] = {0, 1};
int flag = 0; //标记No Answer的情况;
int main(void)
{
int n, i = 1; //i记录case个数;
Primes();
while(scanf("%d", &n), n != 0)
{
printf("Case %d:\n", i++);
if(n != 1 && n % 2 != 0) //素数单独拿出来,减少判断;
{
printf("No Answer\n");
}
else
{
PrimeCirculer(n, 2); //类似于只输出以1开头的全排列的一部分, 所以从2开始, 输出2-n的部分全排列,将下标为1的位置置1;
if(flag == 0)
printf("No Answer\n");
memset(A, 0, sizeof(A));
memset(B, 0, sizeof(B));
A[1] = 1;
B[1] = 1;
flag = 0;
}
}
return 0;
}
void Primes() //素数标记成0, 否则标记为1;
{
int i, j;
for(i = 2; i * i < 40; i++)
{
if(number[i] == 0)
{
for(j = i + i; j < 40; j = j + i)
{
number[j] = 1;
}
}//if
}//for
}
void PrimeCirculer(int n,int cur)
{
int i, j;
if(cur == n + 1 )//
{
if(number[ A[1] + A[n]] == 0)
{
for(i = 1; i <= n; i++)
printf("%d ", A[i]);
flag = 1;//**
printf("\n");
}//if
}
else
{
for(i = 2; i <= n; i++)
{
if(B[i] == 0 && number[ A[cur - 1] + i ] == 0)
{
A[cur] = i;
B[i] = 1;
PrimeCirculer(n, cur + 1);
B[i] = 0;
}
}//for
}//else
}