题目概述
一个由从1开始递增的自然数构成的环,其上共N个数,环上任意相邻两数之和为素数,给定N,以1为环上第一个数,按字典序输出所有满足条件的环
输入
每行一个正整数N
限制
0
输出
每组输出第一行为Case #:其中#为数据序数,第二行为所求的环上的数,两个数之间有一空格,每组输出后有一空行
样例输入
6
8
样例输出
Case 1:
1 4 3 2 5 6
1 6 5 2 3 4Case 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
讨论
没那么复杂,按这数据规模用深搜即可,本来打算偷懒用stl自带的next_permutation函数,不过那个毕竟是全排列,复杂度太高,深搜的话只要干两件事,其一检查是否已达环上要求的数字的个数,其二就是为当前位置分配一个数字
需要注意的是每行最后不能有空格,否则会PE,至于最后一行的空行,没有影响
可能还有更加高效的方法,但是额没打算深究这个题,事实上把所有结果都准备好,按N的值直接输出这些答案,这恐怕是最快的方法,不过这样做毫无意义,额也不打算去尝试
题解状态
717MS,1744K,1138 B,C++
题解代码
#include<stdio.h>
#include<algorithm>
#include<set>
using namespace std;
#define INF 0x3f3f3f3f
#define maxx(a,b) ((a)>(b)?(a):(b))
#define minn(a,b) ((a)<(b)?(a):(b))
#define MAXN 21
#define memset0(a) memset(a,0,sizeof(a))
set<int>prime{ 1,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47 };//数据规模到20为止 因而考虑40以内的素数足以 1实际上并没用上
int N;//环上数字个数
int times = 0;//数据序数 先+1后输出
int nums[MAXN];//存放环上的数
bool marked[MAXN];//检查该数字以否在环上已出现过 这比用set高效不少 但素数用布尔数组会很不直观
bool f;//控制空格输出
inline bool check(int n, int num)//内联的检查相邻两数和的函数 只是为了让代码更清晰而分离出来
{
if (prime.find(nums[n - 1] + num) == prime.end())//不是素数
return false;
return true;
}
void dfs(int n)//深搜 n是当前的位置序号
{
if (n == N) {//如果位数已经足够
if (check(n, 1)) {//最后检查环的两端是否符合 开头肯定是1
for (int p = 0; p < N; p++) {
if (f)
printf(" ");//output
printf("%d", nums[p]);//output
f = 1;
}
printf("\n");//output
f = 0;
return;
}
else//不符合就直接返回
return;
}
for (int p = 1; p <= N; p++)
if (!marked[p] && check(n, p)) {//在环上没出现过 并且满足素数条件
nums[n] = p;//确定这一位的数字
marked[p] = 1;//标记之
dfs(n + 1);//去深搜下一位
marked[p] = 0;//深搜后解除标记
}
}
void fun()
{
printf("Case %d:\n", ++times);//output
nums[0] = 1;//第一个数肯定是1
marked[1] = 1;
dfs(1);//从环上第二个数开始深搜
printf("\n");//output
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
while (~scanf("%d", &N)) {//input
fun();
}
}
EOF