一個筆試題

 題目:輸入一個數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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章