素数环问题
- 问题描述
- 问题分析
- 代码实现
- 生成测试法
- 剪枝法
问题描述
有一个整数n,把从1到n的数字无重复的排列成环,且使每相邻两个数(包括首尾)的和都为素数,称为素数环。
为了简便起见,我们规定每个素数环都从1开始。
输入格式
输入一个整数n
输出格式
输出n个整数,表示一个素数环,如果无解,就输出No Answer
例如
输入
6
输出
1 4 3 2 5 6
1 6 5 2 3 4
输入
3
输出
No Answer
问题分析
首先n是奇数的话一定不为0,因为当n为奇数时,无论如何排列,必定有两个奇数相邻,两奇数相加和为偶数,且一定大于2,所以一定无法组成素数环。
定义一个数组存储素数环,题目要求每个素数环从从1开始,所以第一个数为1,然后从第二个数开始进行深度优先搜索,可以用全排列的方式,每次都先生成排好的数组再判断一下是否是素数环,但是这样明显让搜索走了很多弯路,下面的方法在每一步搜索前做了一个剪枝的操作,判断这条路可不可以走,大大提高了效率。
以下是剪枝法的流程图
代码实现
生成测试法
import java.util.Scanner;
public class PrimeNumberRing2 {
static int[] prime_num; //存储素数环
static int n; //存储素数环的长度
static int cut; //记录可以组成的素数环的个数
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
//当n为奇数时,无论如何排列,必定有两个奇数相邻,
//两奇数相加和为偶数,且一定大于2,所以一定无法组成素数环
if (n % 2 == 1) {
System.out.println("No Answer");
return;
}
//初始化 数组
prime_num = new int[n];
for (int i = 0; i < n; i++) {
prime_num[i] = i + 1;
}
dfs(1); //深度优先搜索
if (cut == 0) { //无解
System.out.println("No Answer");
} else {
System.out.println("共能组成" + cut +"个素数环");
}
}
/**
* 全排列
* @param k 索引
*/
private static void dfs(int k) {
if (k == n) {
for (int i = 0; i < n; i++) {
if (!isPrime(prime_num[i % n] + prime_num[(i + 1) % n])) {
return;
}
}
cut++;
print();
return;
}
for (int i = k; i < n; i++) {
swap(k, i);
dfs(k + 1);
swap(k, i);
}
}
/**
* 交换
*/
private static void swap(int k, int i) {
int temp = prime_num[k];
prime_num[k] = prime_num[i];
prime_num[i] = temp;
}
/**
* 打印素数环
*/
private static void print() {
System.out.print("<" + cut + "> ");
for (int num : prime_num) {
System.out.print(num + " ");
}
System.out.println();
}
/**
* 判断素数
* @param n
* @return
*/
private static boolean isPrime(int n) {
for (int i = 2; i < n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
}
剪枝法
import java.util.Scanner;
public class PrimeNumberRing {
static int[] prime_num; //存储素数环
static int n; //存储素数环的长度
static int cut; //记录可以组成的素数环的个数
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
//当n为奇数时,无论如何排列,必定有两个奇数相邻,
//两奇数相加和为偶数,且一定大于2,所以一定无法组成素数环
if (n % 2 == 1) {
System.out.println("No Answer");
return;
}
prime_num = new int[n];
prime_num[0] = 1; //题目要求第一个数是1
dfs(1); //深度优先搜索
if (cut == 0) { //无解
System.out.println("No Answer");
} else {
System.out.println("共能组成" + cut +"个素数环");
}
}
/**
* 深度优先搜索
* @param i 索引
*/
private static void dfs(int i) {
if (i == n) {
if (isPrime(prime_num[i - 1] + prime_num[0])) {
cut ++;
print();
}
return;
}
for (int k = 2; k <= n; k++) {
if (check(i, k)) { //剪枝
prime_num[i] = k;
dfs(i + 1);
}
}
}
/**
* 检查该数字
* @param i 索引
* @param k 值
* @return
*/
private static boolean check(int i, int k) {
//判断前面是否已经出现过该数字
for (int j = 0; j < i; j++) {
if (prime_num[j] == k) {
return false;
}
}
//判断是否为素数
if (!isPrime(k + prime_num[i - 1])) {
return false;
}
return true;
}
/**
* 打印素数环
*/
private static void print() {
System.out.print("<" + cut + "> ");
for (int num : prime_num) {
System.out.print(num + " ");
}
System.out.println();
}
/**
* 判断素数
* @param n
* @return
*/
private static boolean isPrime(int n) {
for (int i = 2; i < n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
}
测试效果