題目:輸入一個數N,計算1、2、3、...、N這N個數進行排列組合,使得這個數列任意兩個相鄰數之和爲素數,求結果T,T爲這樣的數列的個數;
private static final Map<Integer, Boolean> SUSHU = new HashMap<Integer, Boolean>();
public static void main(String[] args) throws IOException {
method();
}
//獲取標準輸入
private static String[] getInput(int linesCounts) {
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
String line[] = new String[linesCounts];
try {
for (int i = 0; i < linesCounts; i++) {
line[i] = stdin.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
return line;
}
private static void method() {
int N = Integer.parseInt(getInput(1)[0]);
System.out.println(N);
for (int i = 1; i <= N; i++) {
Num num = new Num(i);
for (int j = N; j > 0; j--) {
if (isSuShu(i + j)) {
num.add(j);
}
}
}
int leastNears = Integer.MAX_VALUE;
int startNum = 0;
for (Num num : Num.map.values()) {//找出能與之相鄰的數字最少的數,以儘量減少方法調用次數
if (leastNears > num.near.size()) {
leastNears = num.near.size();
startNum = num.num;
}
}
Num item = Num.map.get(startNum);
Num.startNum = startNum;
System.out.println(Num.linkCounts(item, new HashSet<Integer>()));
}
private static class Num {
static int startNum = 0;//起始數,用以判斷首尾數之和能否爲素數
static Map<Integer, Num> map = new HashMap<Integer, Num>();//
int num;
Set<Integer> near = new HashSet<Integer>();//找出所有能與num相鄰的數
Num(int num) {
this.num = num;
map.put(num, this);
}
void add(int num) {
if (num != this.num) {
near.add(num);
}
}
static int linkCounts(Num thisNum, Set<Integer> removeList) {
int counts = 0;
Set<Integer> newNear = new HashSet<Integer>(thisNum.near);
newNear.removeAll(removeList);
removeList.add(thisNum.num);
if (newNear.size() == 0) {
if (removeList.size() < map.size() || !isSuShu(startNum + thisNum.num)) {
counts = 0;
} else {
counts = 1;
}
} else {
for (int near : newNear) {
Set<Integer> newRemoveList = new HashSet<Integer>(removeList);
newRemoveList.add(thisNum.num);
counts += linkCounts(map.get(near), newRemoveList);
}
}
return counts;
}
}
//判斷一個數是否是素數,放在一個map裏面,以減少計算量
private static boolean isSuShu(int num) {
Boolean suShu = SUSHU.get(num);
if (suShu != null) {
return suShu;
}
suShu = true;
int sqrt = (int) Math.sqrt(num);
for (int i = 2; i < sqrt + 1; i++) {
if (num % i == 0) {
suShu = false;
}
}
SUSHU.put(num, suShu);
return suShu;
}
其實有一個規律,就是當N爲奇數的時候,T肯定爲0,因爲兩個相鄰的數不可能都是奇數,於是就可以不用計算了。
程序還有很多需要小修補的,比如map.size這樣的最好設置一個常量,但是這些不影響算法的實現
這種方法計算結果倒沒問題,但是當N大於等於18的時候就會出現效率問題,因爲遞歸量太大,最後結果超過百萬,也就是linkCounts這個方法至少要計算百萬次,所以在N很大的時候,還需要尋找其它更好的辦法
當N爲20的時候,T好像爲6309600