Uva 1016 Prime ring problem
紫书194页原题 王道计算机考研机试指南147页
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 14715 Accepted Submission(s): 6720
Problem Description
A ring is compose of n circles as shown in diagram. Put natural number 1, 2, …, n into each circle separately, and the sum of numbers in two adjacent circles should be a prime.
Note: the number of first circle should always be 1.
Input
n (0 < n < 20).
Output
The output format is shown as sample below. Each row represents a series of circle numbers in the ring beginning from 1 clockwisely and anticlockwisely. The order of numbers must satisfy the above requirements. Print solutions in lexicographical order.
You are to write a program that completes above process.
Print a blank line after each case.
Sample Input
6
Sample Output
1 4 3 2 5 6
1 6 5 2 3 4
Sample Input
8
Sample Output
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
题目大意为由给定的1到n数字中,将数字依次填入环中,使得环中任意两个相邻的数字间的和为素数。
对于给定的n,按字典序有小到大输出所有符合条件的解。(第一个位置恒定为1)
这就是有名的素数环问题。
为了解决该问题,我们可以采取回朔法枚举每一个值。当第一个数位为1确定时,我们尝试放入第二个数,使其和1的和为素数
,放入后再尝试放入第三个数,使其与第二个数的和为素数,直到所有的数全部放入环中,且最后一个数与1的和也是素数
那么这个方案即为答案,输出;
若在尝试放数的过程中发现当前位置无论放置任何之前未被使用的数君不可能满足条件,那么回溯改变上一个数,
直到产生所需要的正确答案,或者确实不存在更多的解。
题解:输入正整数n,把1—n组成一个环,是相邻的两个整数为素数。输出时从整数1开始,逆时针排列。同一个环恰好输出一次,n (0 < n <= 16)
暴力解法,全部排出来再检查会超时,高效的算法应该是一边放一边检查 ,应用回溯法,可以提早剪枝不免很多不必要的计蒜
java中全局静态变量是没办法进入子函数(直接在子函数中使用)的
//单输入输出
import java.util.Scanner;
public class Main {
static int ans[] = new int[22];//保存环中每一个被放入的数
static boolean hash[] = new boolean[22];//标记之前已经放入环中的数
static int n;
static int prime[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41 };//因为输入不大于16,故两数之和构成的素数必然在数组内
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
for (int i = 0; i < 22; i++) {
hash[i]=false;//初始化标记所有数字为未被使用
}
ans[1]=1;//第一个数字恒定为1
hash[1]=true;//标记1被使用
dfs(1,n);//继续尝试下一个数字
}
public static boolean isPrime(int x) {//这里用其他求素数的方法都可以
boolean flag=false;
for (int i = 0; i < prime.length; i++) {
if (prime[i]==x) {
flag=true;
}
}
return flag;
}
public static void check(int n){//检查输出由回朔法枚举得到的解
if (isPrime(ans[n]+ans[1])==false) {//判断最后一个数与第一个数的和是否为素数,若不是直接返回
return ;
}
for (int i = 1; i <=n; i++) {//输出解
if (i!=1) {
System.out.print(" ");
}
System.out.print(ans[i]);
}
System.out.println();
}
public static void dfs(int num,int n){//递归枚举,num为当前已经放入环中的数据
if (num>1) {//当放入的数字大于一个时
if (isPrime(ans[num]+ans[num-1])==false) {//判断最后两个数字的和是否为素数,若不是则返回,继续枚举第num个数
return ;
}
}
if (num==n) {//若已经放入了n个数
check(n);//检查输出
return ;//返回,继续枚举下一组解
}
for (int i = 2; i <= n; i++) {//放入一个数
if (hash[i]==false) {//若i还没有被放入环中
hash[i]=true;//标记i已经被使用
ans[num+1]=i;//将这个数字放入ans数组中
dfs(num+1,n);//尝试放入下一个数
hash[i]=false;//当回溯回枚举该数字时,将i重新记为未使用
}
}
}
}