素数环问题

  • 问题描述
  • 问题分析
  • 代码实现
  • 生成测试法
  • 剪枝法


问题描述

有一个整数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,然后从第二个数开始进行深度优先搜索,可以用全排列的方式,每次都先生成排好的数组再判断一下是否是素数环,但是这样明显让搜索走了很多弯路,下面的方法在每一步搜索前做了一个剪枝的操作,判断这条路可不可以走,大大提高了效率。

以下是剪枝法的流程图

java用代码生成流程图_java

代码实现

生成测试法

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;
	}
}

测试效果

java用代码生成流程图_java_02