题目出处:《信息学奥赛一本通》例5.1。
题目描述
素数环:从 (1) 到 (n(2 le n le 20)) 这 (n) 个数摆成一个环,要求相邻的两个数的和是一个素数。
输入格式
输入包含一个整数 (n(2 le n le 20)) 。
输出格式
按字典序从小到大的顺序输出所有排列方案,每个排列方案占一行。每行的 (n) 个数之间由一个空格分隔。
样例输入
2
样例输出
1 2
2 1
问题分析
很明显,这是一道可以用搜索解决的问题,我们可以采用“回溯”思想,使用深度优先搜索解决这个问题。
我们用 ans[]
数组来存放我们当前遍历到的答案, ans[id]
用于表示当前排列的第 id 个数是什么。所以我们可以开一个函数 void f(int id)
来表示要在第 id 个位置放数,我只需要从 1 到 n 遍历每一个数(我这里假设是 i),并判断 i 是否能放。
在第 id 个位置能放 i 当且仅当:
- (ans[1]) 到 (ans[id-1]) 都不等于 (i),即 (i) 之前没有放过;
- 当 (id gt 1) 时,满足 (ans[id-1]+ans[id]) 是素数;
- 当 (id = n) 时,满足 (ans[1] + ans[n]) 是素数。
这样,我们递归地调用 f(id)
,当 id>n
时就是我们递归的边界条件;一旦 id>n
就说明我找到了一种方案。
实现代码如下:
#include<bits/stdc++.h>
using namespace std;
int ans[22], n;
bool isp(int a) { // 判断a是否是素数
if (a < 2) return false;
for (int i = 2; i * i <= a; i ++) if (a%i==0) return false;
return true;
}
void output() { // 输出一种排列方案
for (int i = 1; i <= n; i ++)
cout << (i>1 ? " " : "") << ans[i];
cout << endl;
}
void f(int id) { // 搜索函数,在第id个位置尝试放上一个数
if (id > n) { // 边界条件
if (isp(ans[1]+ans[n])) output();
return;
}
for (int i = 1; i <= n; i ++) { // 遍历i = 1 to n ,看看第id个位置能否放i
bool flag = true;
if (id > 1 && !isp(ans[id-1]+i)) flag = false;
if (flag) {
for (int j = 1; j < id; j ++)
if (ans[j] == i) {
flag = false;
break;
}
}
if (flag) {
ans[id] = i;
f(id+1);
}
}
}
int main() {
cin >> n;
f(1);
return 0;
}