题目描述:
- 一个环由n个圈组成,将自然数1-n放入圈内,使得任意相邻圈的两个数之和均为素数。
- 第一个圈的元素均为1。下图为n=6时的一个例子:
- 程序样例
- 输入为一个整数n
- 6
- 8
- 输出分别为
- 1 4 3 2 5 6
- 1 6 5 2 3 4
- 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
解题思路:
- 首先能想到的就是暴力法,全排列然后去筛选结果,但是就是存在效率低的问题。
- 现使用深搜回溯来解决
- 素数判断采用埃筛法
具体代码如下:
/**
* 一个环由n个圈组成,将自然数1-n放入圈内,使得任意相邻圈的两个数之和均为素数。
* 第一个圈的元素均为1。下图为n=6时的一个例子:
* 程序样例
* 输入为一个整数n
* 6
* 8
* 输出分别为
* 1 4 3 2 5 6
* 1 6 5 2 3 4
*
* 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
*/
#include<bits/stdc++.h>
using namespace std;
vector<vector<int>> res;
/**
* @brief 检查tar是不是素数(埃筛法O(n*log(logn)))
* @param tar 要判断是不是素数的目标
* @return 如果tar是素数返回true,否则返回false
*/
bool checkPrim(int tar){
if(tar <= 1) return false;
vector<bool> flag(tar + 1, true); //初始化全为素数
flag[1] = false; //1不是素数
int sq = sqrt(tar);
for(int i = 2; i <= sq; ++i){
if(!flag[i]) continue; //如果当前位置不是素数,继续判断
for(int j = i * i; j <= tar; j += i){
flag[j] = false;
if(j == tar) return false; //中间如果出现不是,就直接返回结果,节省计算
}
}
return flag[tar];
}
/**
* @brief 求解素数环
* @param num 要求解的数,比如题目的6、8等
* @param curNum 当前处理的数
* @param tempRes 处理中间结果
* @param flag 标记那些数已经访问
*/
void helper(const int &num, int curNum, vector<int> &tempRes, unordered_set<int> &flag){
if(tempRes.size() == num && checkPrim(tempRes.back() + 1)){
res.push_back(tempRes);
return;
}
for(int i = 2; i <= num; ++i){
if(0 == flag.count(i) && checkPrim(i + curNum)){
tempRes.push_back(i);
flag.insert(i);
helper(num, i, tempRes, flag);
flag.erase(i);
tempRes.pop_back();
}
}
}
int main(){
vector<int> tempRes;
unordered_set<int> flag;
tempRes.push_back(1);
flag.insert(1);
int num;
cin>>num;
helper(num, 1, tempRes, flag);
for(auto each : res){
copy(begin(each), end(each), ostream_iterator<int>(cout, " "));
cout<<endl;
}
return 0;
}
测试如下:
6
1 4 3 2 5 6
1 6 5 2 3 4
8
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
测试的比较少,大家若测出问题,欢迎私信交流讨论,一起进步!!