華容道再研究

這次我又給自己挖了一個更大的坑,已經確定華容道一共可分爲六個領域,或者叫森林。分別爲0到5 橫式,比如0橫式,表示佈局爲一個曹操,0個橫將,5個豎將,4個兵; 2橫式,表示佈局爲一個曹操,2個橫將,3個豎將,4個兵;

六個森林,一定是沒有任何關係的,獨立的,因爲對於滑塊類遊戲的規則,橫將不可能變化爲豎將,只有位置可以變化。

1) 現在我想找出每個森林共有多少個佈局?
2) 每個森林又有多少顆相互獨立的樹?

相互獨立的樹是什麼?假設一橫式的森林有樹A 和 樹B,那麼樹A的任何節點都不會出現在樹B中,即樹A 和 樹B的交集一定是空集,樹A中的任意佈局通過滑塊遊戲的規則可以演化爲樹A所有的其他佈局,但不可能演化爲樹B中的任意佈局。

那麼,首先就需要一個枚舉類,能夠一個不漏地,並且也一個不多地枚舉出華容道的所有佈局,這樣一個枚舉類是最難以編寫的,幾乎沒有人能一次性寫對,而花多長時間實現這樣一個無bug的枚舉類,可以反映出思維的敏銳度,類似於頭腦的轉速。我比較差,大概花了三天,先開始我以爲只要一天時間就夠了,結果錯的離譜,因爲後來我發現,我想得還不是很清楚,就已經開始編碼了,結果編寫都一半時,發現一個難題,”怎麼樣才能比較優雅地從當前佈局推出下一個佈局呢?”

寫到一半又發現,靠,之前的處理辦法是錯誤的,又要推倒重來,
嗯,這種情況當時沒有考慮到…

一旦正確地枚舉出了所有的佈局,問題就簡單了,首先問題1有了答案,已經有華容道自動求解的現成代碼,只要稍作修改,就能根據一個佈局,得到包含這個佈局的一顆樹。有了從一個佈局得到一顆樹的方法,問題2也解決了,還可以判斷一個森林,其中有多少顆樹是有曹出佈局的。

怎麼枚舉?用字典序來枚舉,就是定義一個枚舉規則,然後給計算機任意一個有效佈局(可以放置1個大王,5個橫將或者豎將,4個兵),計算機都能推出其下一個或者上一個佈局,根據這個枚舉規則,每個佈局都有了一個唯一地,一一對應地序列號。

我是這樣定義枚舉規則的,首先放置大王,然後放置橫將,然後放置豎將,最後放置4個兵,然後給4列5行的棋盤就行編號,
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
16 17 18 19
對於 一橫式,第一個佈局是:

    // 5 5 4 4 | 0 1 2 3
    // 1 1 3 3 | 4 5 6 7
    // 3 3 1 1 | 8 9 10 11
    // 1 1 0 0 | 12 13 14 15
    // 0 0 0 0 | 16 17 18 19

最後一個佈局是:

    // 0 0 0 0 | 0 1 2 3
    // 0 0 3 3 | 4 5 6 7
    // 3 3 1 1 | 8 9 10 11
    // 1 1 5 5 | 12 13 14 15
    // 4 4 1 1 | 16 17 18 19

爲了代碼更清晰簡潔,先不考慮4個兵的放置,因爲前面關於華容道自動求解的盤面編碼類的代碼,已經有了關於4個兵的組合序號表了,直接拿來用。

這個枚舉規則,保證兩個空格總是逐漸地 從高位移動到低位,
而相應地,大王,橫將豎將總是逐漸地 從低位移動到高位。

package game.hrd.game.hrd.refactor;

import game.hrd.LocalConst;

/** * Created by huangcongjie on 2017/12/24. */
public class LayoutEnumerator {
    private int hNum = 0; //橫將個數 只能在構造函數初始化
    private int sNum = 0; //豎將個數 只能在構造函數初始化
    public int mIndex = -1;  //大王的位置

    private int[] layout;
    private int[][] movableS, movableH;
    private int[] sCursors, hCursors;

    int[] WQ = LocalConst.WQ2;
    private int[] binCombineArr = new int[15];
    private int count = 0;

    public LayoutEnumerator(int hCount) {
        if (hCount < 0 || hCount > 5) {
            throw new RuntimeException("invalid horizontal piece number! the hCount should be [0, 5]");
        }
        setBinCombineArr();
        hNum = hCount;
        sNum = 5 - hCount;
        sCursors = new int[sNum];
        hCursors = new int[hNum];
        movableS = new int[sNum][];
        movableH = new int[hNum][];
        mIndex = 0;

        layout = new int[20];
        layout[mIndex] = layout[mIndex + 1] = 5;
        layout[mIndex + 4] = layout[mIndex + 5] = 1;
        initHorizontalArrs(layout, 0, 0);
        initVerticalArrs(layout, 0, 0);
    }

    public LayoutEnumerator next() {
        //小兵移不動了,開始移動豎將
        if (sNext()) {
            count++;
            //移動豎將成功
            return this;
        }
        //豎將移不動了,開始移動橫將
        if (hNext()) {
            boolean moved = true;
            while ( !initVerticalArrs(layout, 0, 0) ) {
                //橫將的擺放,讓豎將沒位置了
                for (int i = 0; i < layout.length; i++) {
                    if (layout[i] == 3) {
                        layout[i] = layout[i + 4] = 0;
                    }
                }
                moved = hNext();
                if (!moved) {
                    //橫將移不動了
                    break;
                }
            }
            if (moved) {
                count++;
                //移動橫將成功
                return this;
            }
        }
        //橫將移不動了,開始移動大王
        mIndex++;
        if (LocalConst.COL[mIndex] == 3) {
            mIndex++;
        }
        if (mIndex > 14) {
            //大王都移不動了,所有佈局都按字典序列枚舉完畢
            return null;
        } else {
            count++;
            //移動大王成功,開始初始化 橫將,豎將,空格和小兵
            layout = new int[20];
            layout[mIndex] = layout[mIndex + 1] = 5;
            layout[mIndex + 4] = layout[mIndex + 5] = 1;
            initHorizontalArrs(layout, 0, 0);
            initVerticalArrs(layout, 0, 0);
            return this;
        }
    }


    // 3 5 5 3 | 0 1 2 3
    // 1 1 1 1 | 4 5 6 7
    // 3 4 4 3 | 8 9 10 11
    // 1 0 0 1 | 12 13 14 15
    // 0 0 0 0 | 16 17 18 19
    //-------------------------
    // 0 3 4 7 8 11 12 13 14 15 >
    // 0 3 4 7 8 11 12
    // 3 4 7 8 11 12 13
    // 8 11 12 13 14
    // 11 12 13 14 15
    private boolean sNext() {
        int i = sNum - 1;
        boolean flag = false;
        for (; i >=0; i--) {
            int loc = movableS[i][sCursors[i]];
            layout[loc] = layout[loc + 4] = 0;
            int a = sCursors[i] + 1;
            for (int j = a; j < movableS[i].length; j++) {
                int loc2 = movableS[i][j];
                if (layout[loc2] == 0 && layout[loc2 + 4] == 0) {
                    sCursors[i] = j;
                    flag = true;
                    break;
                }
            }
            if (flag) {
                break;
            }
        }
        if (flag) {
            int loc = movableS[i][sCursors[i]];
            layout[loc] = 3;
            layout[loc + 4] = 1;
            //change movableS[i+] and set sCursors[i+] = 0;
            initVerticalArrs(layout, loc + 1, i + 1);
        }
        return flag;
    }

    private boolean hNext() {
        int i = hNum - 1;
        boolean flag = false;
        for (; i >=0; i--) {
            int loc = movableH[i][hCursors[i]];
            layout[loc] = layout[loc + 1] = 0;
            int a = hCursors[i] + 1;
            for (int j = a; j < movableH[i].length; j++) {
                int loc2 = movableH[i][j];
                if (layout[loc2] == 0 && layout[loc2 + 1] == 0) {
                    hCursors[i] = j;
                    flag = true;
                    break;
                }
            }
            if (flag) {
                break;
            }
        }
        if (flag) {
            int loc = movableH[i][hCursors[i]];
            layout[loc] = 4;
            layout[loc + 1] = 4;
            initHorizontalArrs(layout, loc + 2, i + 1);
        }
        return flag;
    }

    private boolean initVerticalArrs(int[] layout, int startLoc, int index) {
        if (index >= sNum) {
            return true;
        }
        sCursors[index] = 0;
        int[] movableLst = new int[12];
        int movableCount = 0;
        for (int i = startLoc; i < layout.length; i++) {
            if (LocalConst.ROW[i] < 4 &&
                    layout[i] == 0 && layout[i+4] == 0) {
                movableLst[movableCount] = i;
                movableCount++;
            }
        }
        if (movableCount == 0) {
            //橫將的擺放不合法,因爲使得有豎將無法擺放
            return false;
        }

        int sCount = sNum - index;
        int[] temp = HrdUtil.copy(layout);
        int[] rArr = new int[sCount];
        int num = 0;
        for (int i = 15; i >= 0; i--) {
            if (temp[i] == 0 && temp[i+4] == 0) {
                rArr[num] = i;
                num++;
                if (sCount == num) {
                    break;
                }
                temp[i] = 3;
            }
        }

        int[] locs = new int[12];
        int n = 0;
        for (int j = 0; j < movableCount; j++) {
            int max = rArr[sCount - 1];
            int loc = movableLst[j];
            if (loc <= max) {
                locs[n] = loc;
                n++;
            }
        }
        int[] locations = new int[n];
        for (int m = 0; m < n; m++) {
            locations[m] = locs[m];
        }
        movableS[index] = locations;
        int firstLoc = movableLst[0];
        layout[firstLoc] = 3;
        layout[firstLoc + 4] = 1;
        return initVerticalArrs(layout, firstLoc + 1, index + 1);
    }

    private void initHorizontalArrs(int[] layout, int startLoc, int index) {
        if (index >= hNum) {
            return;
        }
        hCursors[index] = 0;

        int[] movableLst = new int[11];
        int movableCount = 0;
        for (int i = startLoc; i < 20; i++) {
            if (LocalConst.COL[i] < 3 &&
                    layout[i] == 0 && layout[i+1] == 0) {
                movableLst[movableCount] = i;
                movableCount++;
            }
        }
        int hCount = hNum - index;
        int num = 0;
        int[] rArr = new int[hCount];
        for (int i = 18; i >= 0; i--) {
            if (LocalConst.COL[i] < 3 && layout[i] == 0 && layout[i+1] == 0) {
                rArr[num] = i;
                num++;
                if (hCount == num) {
                    break;
                }
                i--;
            }
        }
        int[] locs = new int[11];
        int n = 0;
        for (int j = 0; j < movableCount; j++) {
            int max = rArr[hCount - 1];
            int loc = movableLst[j];
            if (loc <= max) {
                locs[n] = loc;
                n++;
            }
        }
        int[] locations = new int[n];
        for (int m = 0; m < n; m++) {
            locations[m] = locs[m];
        }
        movableH[index] = locations;
        int firstLoc = movableLst[0];
        layout[firstLoc] = layout[firstLoc + 1] = 4;
        initHorizontalArrs(layout, firstLoc + 2, index + 1);
    }

    private void initVerticalArrs3(int[] layout, int startLoc, int sCount) {
        if (sCount < 1)
            return;
        for (int i = 0; i < sCount; i++) {
            sCursors[sNum - 1 - i] = 0;
        }
        int[] movableLst = new int[12];
        int movableCount = 0;
        for (int i = startLoc; i < layout.length; i++) {
            if (LocalConst.ROW[i] < 4 &&
                    layout[i] == 0 && layout[i+4] == 0) {
                movableLst[movableCount] = i;
                movableCount++;
            }
        }
        int[] temp = HrdUtil.copy(layout);
        int[] arr = new int[sCount];
        int num = 0;
        for (int i = startLoc; i < temp.length; i++) {
            if (LocalConst.ROW[i] < 4 &&
                    temp[i] == 0 && temp[i+4] == 0) {
                arr[num] = i;
                num++;
                if (sCount == num) {
                    break;
                }
                temp[i+4] = 1;
            }
        }
        temp = HrdUtil.copy(layout);
        int[] rArr = new int[sCount];
        num = 0;
        for (int i = 15; i >= 0; i--) {
            if (temp[i] == 0 && temp[i+4] == 0) {
                rArr[num] = i;
                num++;
                if (sCount == num) {
                    break;
                }
                temp[i] = 3;
            }
        }
        int offset = sNum - sCount;
        for (int i = 0; i < sCount; i++) {
            int[] locs = new int[12];
            int n = 0;
            for (int j = 0; j < movableCount; j++) {
                int max = 99;
                if (i < sCount - 1) {
                    max = rArr[sCount - 1 - i];
                }
                int min = -1;
                if (i > 0) {
                    min = arr[i];
                }
                int loc = movableLst[j];
                if (loc >= min && loc <= max) {
                    locs[n] = loc;
                    n++;
                }
            }
            int[] locations = new int[n];
            for (int m = 0; m < n; m++) {
                locations[m] = locs[m];
            }
            movableS[offset + i] = locations;
        }
        for (int i = 0; i < arr.length; i++) {
            int loc = arr[i];
            layout[loc] = 3;
            layout[loc + 4] = 1;
        }
    }
    // 4 4 4 4 | 0 1 2 3
    // 5 5 4 4 | 4 5 6 7
    // 1 1 0 0 | 8 9 10 11
    // 0 0 0 0 | 12 13 14 15
    // 0 0 0 0 | 16 17 18 19
    //--------------------------------
    // 0 1 2 6 10 12 13 14 16 17 18 >
    // 0 1 2 6 10 12 13 14
    // 2 6 10 12 13 14 16
    // 6 10 12 13 14 16 17 18

    public int[] getLayout() {
        return layout;
    }

    private void setBinCombineArr() {
        int m = 0;
        for (int i = 1; i < 63; i++) {
            int k = 0;
            for (int j = 0; j < WQ.length; j++) {
                if ( (i & WQ[j]) > 0) {
                    k++;
                }
            }
            if (k == 4) {
                binCombineArr[m] = i;
                m++;
            }
        }
    }

    public int[][] getBin15Layouts() {
        int[] locations = new int[6];
        int m = 0;
        for (int i = 0; i < layout.length; i++) {
            if (layout[i] == 0 || layout[i] == 2) {
                locations[m] = i;
                m++;
            }
        }
        int[][] ret = new int[15][];
        int k1 = -1, k2 = -1;
        for (int i = 0; i < binCombineArr.length; i++) {
            int[] bingLayout = HrdUtil.copy(layout);
            int combo = binCombineArr[i];
            for (int j = 0; j < 6; j++) {
                if ( (combo & WQ[j]) > 0) {
                    bingLayout[locations[j]] = 2;
                } else {
                    k1 = k2;
                    k2 = locations[j];
                }
            }
            bingLayout[k1] = bingLayout[k2] = 0;
            ret[i] = bingLayout;
        }
        return ret;
    }

    public static void test() {
        int sum = 0;
        for (int i = 0; i < 6; i++) {
            int hCount = i;
            LayoutEnumerator enumerator = new LayoutEnumerator(hCount);
            System.out.print(enumerator.getPrintLayout2());
            int total = 1;
            while ((enumerator.next() != null)) {
                total ++;
// System.out.print(enumerator.getPrintLayout2());
            }
            System.out.println(String.format("%d橫式共有%d節點", hCount, total));
            sum += total;
        }
        System.out.println(String.format("華容道共有%d節點", sum));
    }

    public String getPrintLayout2() {
        String layoutStr = "";
        for (int i = 0; i < 20; i++) {
            layoutStr += layout[i] + "\t";
            if (i % 4 == 3) {
                layoutStr += "\n";
            }
        }
        layoutStr = layoutStr.substring(0, layoutStr.length() - 1);
        layoutStr += "\n---------" + count + "-----------\n\n";
        return layoutStr;
    }

    public static String getPrintLayout(int[] layout, int count) {
        String layoutStr = "";
        for (int i = 0; i < 20; i++) {
            layoutStr += layout[i] + "\t";
            if (i % 4 == 3) {
                layoutStr += "\n";
            }
        }
        layoutStr = layoutStr.substring(0, layoutStr.length() - 1);
        layoutStr += "\n---------" + count + "-----------\n\n";
        return layoutStr;
    }

    public static String getPrintLayout2(int[] layout, int count) {
        String layoutStr = "";
        for (int i = 0; i < 20; i++) {
            layoutStr += layout[i] + ", ";
            if (i % 4 == 3) {
                layoutStr += "\n";
            }
        }
        layoutStr = layoutStr.substring(0, layoutStr.length() - 1);
        layoutStr += "\n---------" + count + "-----------\n\n";
        return layoutStr;
    }

    static public String toLayoutString(int[] layout2) {
        String layout = "";
        for (int i = 0; i < 20; i++) {
            layout += layout2[i];
            if (i % 4 == 3) {
                layout += ", ";
            } else {
                layout += " ";
            }
        }
        layout = layout.substring(0, layout.length() - 2);
        return layout;
    }

    // 3 5 5 3 | 0 1 2 3
    // 1 1 1 1 | 4 5 6 7
    // 3 4 4 3 | 8 9 10 11
    // 1 0 0 1 | 12 13 14 15
    // 0 0 0 0 | 16 17 18 19

    // 0 3 4 7 8 11 12 13 14 15 >
    // 0 3 4 7 8 11 12
    // 3 4 7 8 11 12 13
    // 8 11 12 13 14
    // 11 12 13 14 15

    // 3 3 3 3 | 0 1 2 3
    // 1 1 1 1 | 4 5 6 7
    // 0 4 4 0 | 8 9 10 11
    // 5 5 0 0 | 12 13 14 15
    // 1 1 0 0 | 16 17 18 19

    // 0 1 2 3 4 7 11 14 15 >
    // 0 1 2 3 4
    // 1 2 3 4 7
    // 2 3 4 7 11 14
    // 4 7 11 14 15

    // 4 4 4 4 | 0 1 2 3
    // 5 5 4 4 | 4 5 6 7
    // 1 1 0 0 | 8 9 10 11
    // 0 0 0 0 | 12 13 14 15
    // 0 0 0 0 | 16 17 18 19

    // 0 1 2 6 10 12 13 14 16 17 18 >
    // 0 1 2 6 10 12 13 14
    // 2 6 10 12 13 14 16
    // 6 10 12 13 14 16 17 18
}

代碼講解:
movableS,movableH是二維數組
最核心的代碼方法,
boolean initVerticalArrs() 初始化所有豎將的可行位置數組,movableS, 所有豎將放置成功則返回true
initHorizontalArrs()初始化所有橫將的可行位置數組,movableH

initVerticalArrs的功能:
如果給定一個佈局的大王位置,橫將位置,則所有豎將能放置在哪些位置都要儘可能地確定下來,確定每個豎將最小和最大的可放置位置。

initHorizontalArrs的功能:
如果給定一個佈局的大王位置,則所有橫將能放置在哪些位置都要儘可能地確定下來。

重要的代碼方法
int[] sCursors, hCursors; 分別表示豎將的遊標,橫將的遊標
sNext() 移動豎將,成功則返回true,當所有豎將移動到頭時,返回false,負責更新豎將的遊標
hNext() 移動橫將,返回值同sNext(),負責更新橫將的遊標

舉個例子

    // 3 5 5 3 | 0 1 2 3
    // 1 1 1 1 | 4 5 6 7
    // 3 4 4 3 | 8 9 10 11
    // 1 0 0 1 | 12 13 14 15
    // 0 0 0 0 | 16 17 18 19

    // 0 3 4 7 8 11 12 13 14 15 >
    // 0 3 4 7 8 11 12
    // 3 4 7 8 11 12 13
    // 8 11 12 13 14
    // 11 12 13 14 15

對於橫刀立馬布局,已經給定了大王,關羽的位置,那麼所有豎將的可選位置是 // 0 3 4 7 8 11 12 13 14 15 >
第一個豎將 0 3 4 7 8 11 12
第二個豎將 3 4 7 8 11 12 13
第三個豎將 8 11 12 13 14
第四個豎將 11 12 13 14 15
initVerticalArrs() 負責把movableS初始化爲以上那個二維數組,
不僅要推出所有豎將的可選位置,還要知道針對每個豎將,哪些位置是已經被其他豎將佔用了,不能在放置了,如第一個一定不能放置在13 14 15因爲已經被後三個佔用了
第二個一定不能放0 14 15,第三第四個同理
還有三點要強調,(都是走過的坑)
第一點,initVerticalArrs爲什麼是遞歸的,我曾經試圖把它改爲非遞歸的,結果幾個小時都沒有搞定,而且各種錯誤,非常煩,因爲其中的局部變量 rArr, 是動態的,不能在一次循環中,正確地初始化,必須要在前面豎將已經放置好的情況下才能,initVerticalArrs3() 方法中去初始化rArr是不正確的。
比如

    // 5 5 0 0 | 0 1 2 3
    // 1 1 0 0 | 4 5 6 7
    // 4 4 3 0 | 8 9 10 11
    // 3 3 1 3 | 12 13 14 15
    // 1 1 0 1 | 16 17 18 19

如果第二個豎將不確定放置在11 還是 12,那麼最後兩個豎將也不能確定怎麼放

第二點,如何更新二維數組movableS ?還是以上面的橫刀立馬爲例,假設
第三個豎將 8 11 12 13 14
第四個豎將 11 12 13 14 15
第四個豎將已經走到15,第三個豎將 需要從8走到11,那麼第四個豎將的可放置位置都要改變,並且其對應的遊標置0,sCursors[3] = 0, movableS[3]的所有元素都要更新,
如果第一個豎將走到下一個位置,第二三四個豎將所有的可放置數組都要更新,並且相應地遊標置0

如果橫將走到下一個位置,整個二維數組movableS都要改,sCursors的所有元素置0

第三點,爲什麼initVerticalArrs() 有時爲返回false,因爲大王,橫將的擺放,有時會使得豎將放不下了,比如如下二橫式,怎麼放第三個豎將?無解,那麼橫將必須的進行下一種擺放

    // 0 0 0 0 | 0 1 2 3
    // 5 5 4 4 | 4 5 6 7
    // 1 1 0 0 | 8 9 10 11
    // 0 0 4 4 | 12 13 14 15
    // 0 0 0 0 | 16 17 18 19

如果曹操走到下一個位置,整個二維數組movableS和movableH都要改,sCursors和hCursors的所有元素置0

針對哈希佈局的盤面編碼類

package game.hrd.game.hrd.refactor;

import game.hrd.LocalConst;

//針對哈希佈局的盤面編碼類
public class HashLayoutEncoder {

    //組合序號表
    short[] Hz;     //橫將
    short[] Sz;     //豎將
    short[] Bz;     //小兵

    //權值表
    int[] Hw;
    int[] Sw;
    int[] Mw;

    public int bmTotal;

    public HashLayoutEncoder(int[] layout) {
        Hz = new short[4096 * 3];
        Sz = new short[4096 * 3];
        Bz = new short[128];
        //C12取5=792
        Hw = new int[792*2];
        Sw = new int[792];
        int i,j,k;
        int Hn=0, Bn=0, Sn=0; //各類子數目,大王默認爲1不用計數
        for(i=0;i<20;i++){   //計算各種棋子的個數
            if(layout[i] == LocalConst.B) Bn++;
            if(layout[i] == LocalConst.H) Hn++;
            if(layout[i] == LocalConst.S) Sn++;
        }
        Hn /= 2;
        int[] WQ2 = LocalConst.WQ2;
// int Hmax=WQ2[11],Smax=WQ2[12-Hn*2],Bmax=WQ2[16-(Hn+Sn)*2]; //各種子的最大二進位數
        int Hmax=WQ2[11],Smax=WQ2[12],Bmax=WQ2[6]; //各種子的最大二進位數
        short Hx=0,Sx=0,Bx=0; //各種棋子組合的最大序號
        //初始化組合序號表
        for(i=0; i<4096; i++){
            for(j=0,k=0;j<12;j++) {
                if((i & WQ2[j]) > 0) {
                    k++; //計算1的個數
                }
            }
            if(k==Hn && i<Hmax) {
                Hz[i] = Hx++;
            }
            if(k==Sn && i<Smax) {
                Sz[i]=Sx++;
            }
            if(k==Bn && i<Bmax) {
                Bz[i]=Bx++;
            }
        }
        int Sq=Bx,Hq=Bx*Sx,Mq=Bx*Sx*Hx; //豎將位權,橫將位權,王位權
        Mw = new int[12];
        Hw = new int[Hx];
        Sw = new int[Sx];
        for(i=0;i<12;i++) Mw[i]=i*Mq; //初始化大王權值表
        for(i=0;i<Hx;i++) Hw[i]=i*Hq; //初始化橫將權值表
        for(i=0;i<Sx;i++) Sw[i]=i*Sq; //初始化豎將權值表
        bmTotal = Mq*12;
    }

    //盤面編碼
    public int encode(int[] layout) {
        int Bb=0,Bd=-1; //空位序號記錄器
        int Sb=0,Sd=-1; //豎條序號記錄器
        int Hb=0,Hd=-1; //橫條序號記錄器
        int Mb = 0;         //大王序號記錄器
        int c;
        int[] f = new int[20];

        for(int i = 0; i < 20; i++){
            c=layout[i];
            if(c == LocalConst.K) { //大王定序
                if(f[i] == 0) {
                    Mb = i - LocalConst.ROW[i];
                    f[i] = f[i + 1] = f[i + 4] = f[i + 5] = 1;
                }
                continue;
            }
            if (LocalConst.COL[i]<3 && layout[i+1] <= LocalConst.H) {
                if (LocalConst.ROW[i] < 1) {
                    Hd++; //橫條位置序號(編號)
                } else if (layout[i-4] <= LocalConst.H &&
                    layout[i-3] <= LocalConst.H) {
                    Hd++; //橫條位置序號(編號)
                }
            }
            if (c == LocalConst.H) {//橫將定序,轉爲二進制進行詢址得Hb
                if(f[i] == 0) {
                    Hb += LocalConst.WQ2[Hd];
                    f[i] = f[i+1] = 1;
                }
                continue;
            }
            if (LocalConst.ROW[i]<4 && layout[i+4]<=LocalConst.S) {
                if (LocalConst.ROW[i] < 1) {
                    Sd++; //豎將位置序號(編號)
                } else if (layout[i-4] <= LocalConst.H) {
                    Sd++; //豎將位置序號(編號)
                }
            }
            if (c == LocalConst.S) { //豎條定序,轉爲二進制進行詢址得Sb
                if(f[i] == 0) {
                    Sb += LocalConst.WQ2[Sd];
                    f[i] = f[i+4] = 1;
                }
                continue;
            }
            if(c == LocalConst.B || c == 0)
                Bd++;  //小兵位置序號(編號)
            if(c == LocalConst.B)
                Bb += LocalConst.WQ2[Bd]; //小兵定序,轉爲二進制進行詢址得Bb
        }
        //Hb,Sb,Bb爲組合序號,"橫刀立馬"最大值爲小兵C(6,4)-1=15-1,豎條C(10,4)-1=210-1
        Bb=Bz[Bb];
        Sb=Sz[Sb];
        Hb=Hz[Hb];//詢址後得得Bb,Sb,Hb組合序號
        return Bb+Sw[Sb]+Hw[Hb]+Mw[Mb]; //用位權編碼,其中Bb的位權爲1
    }

    //按左右對稱規則考查棋盤,對其編碼
    public int symmetricEncode(int[] q){
        char i;
        int[] q2 = new int[20];
        for(i=0; i<20; i+=4) {
            q2[i]=q[i+3];
            q2[i+1]=q[i+2];
            q2[i+2]=q[i+1];
            q2[i+3]=q[i];
        }
        return encode(q2);
    }

    public static String getPrintLayout(int[] layout) {
        String layoutStr = "";
        for (int i = 0; i < 20; i++) {
            layoutStr += layout[i] + "\t";
            if (i % 4 == 3) {
                layoutStr += "\n";
            }
        }
        layoutStr = layoutStr.substring(0, layoutStr.length() - 1);
        return layoutStr;
    }
}
package game.hrd.game.hrd.refactor;

import game.hrd.LocalConst;

/** * Created by huangcongjie on 2017/12/20. */
public class HashLayoutAnalyzer implements IAnalyzer {
    private int k1 = -1; //空格1位置
    private int k2 = -1; //空格2位置
    private int h;  //兩空格的聯合類型, {0:不聯合,1:豎聯合,2:橫聯合}

    @Override
    public void analyze(HrdNode hrdLayout) {
        int i=0; //i,列
        k1 = k2 = -1;
        h = 0;
        int[] layout = hrdLayout.layout;
        for(i=0; i<20; i++){
            if(layout[i] == 0) {
                if (k1 == -1) {
                    k1 = i;
                } else {
                    k2 = i;
                    break;
                }
            }
        }
        if (k1 + 4 == k2) {
            h = 1;  //空格豎聯合
        }
        if (k1 + 1 == k2 && LocalConst.COL[k1] < 3) {
            h = 2;  //空格橫聯合
        }
        int col1 = LocalConst.COL[k1];
        int col2 = LocalConst.COL[k2];
        if (col1 > 0) {
            i = k1 - 1;
            zinb(hrdLayout, i, k1);
            if (layout[i] == 3) {
                if (h == 1)
                    hrdLayout.addChoice(i, k1);
            }
            if (layout[i] == 5) {
                if (h == 1)
                    hrdLayout.addChoice(i - 1, k1 - 1);
            }
            if (layout[i] == 4) {
                if (h == 2) {
                    hrdLayout.addChoice(i - 1, k2 - 1);
                }
                hrdLayout.addChoice(i - 1, k1 - 1);
            }
        }
        if (col1 < 3) {
            i = k1 + 1;
            zinb(hrdLayout, i, k1);
            if (layout[i] == 3) {
                if (h == 1)
                    hrdLayout.addChoice(i, k1);
            }
            if (layout[i] == 5) {
                if (h == 1)
                    hrdLayout.addChoice(i, k1);
            }
            if (layout[i] == 4) {
                hrdLayout.addChoice(i, k1); //如果橫聯合,k1不是第一空,所以不用判斷h
            }
        }
        if (k1 > 3) {
            i = k1 - 4;
            zinb(hrdLayout, i, k1);
            if (layout[i] == 4 && layout[i+1] == 4 &&
                    (col1 != 1 || layout[i-1] != 4) ) {
                if (h == 2)
                    hrdLayout.addChoice(i, k1);
            }
            if (layout[i] == 1) {
                if (layout[i-4] == 3) {
                    if (h == 1) {
                        hrdLayout.addChoice(i - 4, k2 - 4);
                    }
                    hrdLayout.addChoice(i - 4, k1 - 4);
                }
                if (layout[i-4] == 5 && layout[i-3] == 5) {
                    if (h == 2)
                        hrdLayout.addChoice(i - 4, k1 - 4);
                }
            }
        }
        if (k1 < 16) {
            i = k1 + 4;
            zinb(hrdLayout, i, k1);
            if (layout[i] == 3)
                hrdLayout.addChoice(i, k1);
            if (layout[i] == 4 && i < 19 && layout[i+1] == 4 &&
                    (col1 != 1 || layout[i-1] != 4) ) {
                if (h == 2) {
                    hrdLayout.addChoice(i, k1);
                }
            }
            if (layout[i] == 5 && layout[i+1] == 5) {
                if (h == 2)
                    hrdLayout.addChoice(i, k1);
            }
        }
        if (col2 > 0) {
            i = k2 - 1;
            zinb(hrdLayout, i, k2);
            if (layout[i] == 4) {
                hrdLayout.addChoice(i - 1, k2 - 1);
            }
        }
        if(k2>3)  {
            i = k2 - 4;
            zinb(hrdLayout, i, k2);
            if(layout[i]==1 && layout[i-4] == 3) {
                hrdLayout.addChoice(i - 4, k2 - 4);
            }
        }
        if(col2<3) {
            i = k2 + 1;
            zinb(hrdLayout, i, k2);
            if(layout[i]==4) {
                if(h==2) {
                    hrdLayout.addChoice(i, k1);
                }
                hrdLayout.addChoice(i, k2);
            }
        }
        if(k2<16) {
            i = k2 + 4;
            zinb(hrdLayout, i, k2);
            if(layout[i]==3) {
                if(h==1) {
                    hrdLayout.addChoice(i, k1);
                }
                hrdLayout.addChoice(i, k2);
            }
        }
    }

    private void zinb(HrdNode hrdLayout, int src, int dst) {
        if (hrdLayout.layout[src] == 2) {
            //小兵
            if (h > 0) {
                hrdLayout.addChoice(src, k1);
                hrdLayout.addChoice(src, k2);
            } else {
                hrdLayout.addChoice(src, dst);
            }
        }
    }

    //走一步函數
    @Override
    public void step(int[] layout, int src, int dst) {
        int c = layout[src];
        int lx = c;
        if (c == 1) {
            lx = layout[src-4];
        }
        switch(lx) {
            case 2:     //兵
                layout[src] = 0;
                layout[dst] = c;
                break;
            case 3:     //豎
                layout[src] = layout[src+4] = 0;
                layout[dst] = c;
                layout[dst+4] = 1;
                break;
            case 4:     //橫
                layout[src] = layout[src+1]=0;
                layout[dst] = layout[dst+1]=c;
                break;
            case 5:     //王
                layout[src] = layout[src+1]= layout[src+4]=layout[src+5]=0;
                layout[dst] = layout[dst+1]= c;
                layout[dst+4]=layout[dst+5]= 1;
                break;
        }
    }
}
package game.hrd.game.hrd.refactor;

public class HrdNode {
    public int[] layout;
    public int level = 0;
    public HrdNode parent = null;
    //記錄上次移動的索引
    private int preMovedIndex = -1;

    //原位置,目標位置,最多隻會有10步
    public int[] srcArr = new int[10];
    public int[] dstArr = new int[10];
    public int totalChoices = 0;

    private IAnalyzer analyzer;
    private boolean isHashLayout;

    public int nodeNum=1;
    public boolean outable = false;
    public int hNum;
    private int dstCode = -1;
    public int bmCode = -2;

    public void setGoal(int code) {
        dstCode = code;
    }

    public HrdNode(int[] layout, IAnalyzer analyzer, boolean useHash) {
        this.layout = layout;
        this.analyzer = analyzer;
        isHashLayout = useHash;
        analyzer.analyze(this);
    }

    public void addChoice(int src, int dst) {
        srcArr[totalChoices] = src;
        dstArr[totalChoices] = dst;
        totalChoices++;
    }

    public boolean isGoal() {
        if (dstCode >= 0)  {
            return bmCode == dstCode;
        }
        return layout[13] == 5 && layout[14] == 5;
    }

    public HrdNode giveBirth(IChecker encoder, Solver.IListener listener) {
        HrdNode answer = null;
        for (int i = 0; i < totalChoices; i++) {
            if (srcArr[i] == preMovedIndex) {
                //和上次移動同一個棋子時不搜索,可提速20%左右
                continue;
            }
            int[] childLayout = HrdUtil.copy(layout);
            analyzer.step(childLayout, srcArr[i], dstArr[i]);
            if (encoder.checkAndModify(childLayout)) {
                //get permission to giveBirth
                HrdNode child = new HrdNode(childLayout, analyzer, isHashLayout);
                child.bmCode = encoder.getCurrentCode();
                child.parent = this;
                child.setGoal(dstCode);
                child.level = this.level + 1;
                child.setPreMovedIndex(dstArr[i]);
                if (listener != null) {
                    listener.validNodeCreated(child);
                    if (child.isGoal()) {
                        answer = child;
                    }
                }
            }
        }
        return answer;
    }

// public int getValidChildrenNum() {
// return validChildrenNum;
// }

    public boolean isHashLayout() {
        return isHashLayout;
    }

    public void setPreMovedIndex(int preMovedIndex) {
        this.preMovedIndex = preMovedIndex;
    }
}
package game.hrd.game.hrd.refactor;

import game.hrd.PmBm;

import java.util.ArrayList;

/** * Created by huangcongjie on 2017/12/29. */
public class TreeFinder {
    HrdNode[] queue;
    int searchAmount = 0;
    int processingIndex = 0;
    int maxAmount = 500000;
    private int[] flagMap;
    private int[][] layoutMap = new int[500000][20];
    private int[] codeSnMap;    //根據編碼找序列號

    private void addToQueue(HrdNode node, int nodeCode, int dcCode) {
        if (searchAmount >= maxAmount) {
            System.out.println("對溢出");
            return;
        }
        queue[searchAmount] = node;
        searchAmount++;
        markSearched(nodeCode);
        markSearched(dcCode);
    }

    //--廣度優先--
    private HrdNode find(int[] layout) {
        long t1 = System.currentTimeMillis();
        long cost = 0;
        IAnalyzer analyzer = new HashLayoutAnalyzer();
        HrdNode root = new HrdNode(layout, analyzer, false);
        if (root.isGoal()) {
            root.outable = true;
        }
        IChecker checker = new LayoutToCode(root.layout);
        maxAmount = ((LayoutToCode) checker).codeTotalNum / 10;
        queue = new HrdNode[maxAmount];
        searchAmount = processingIndex = 0;
        HrdNode curNode = root;
        checker.checkAndModify(root.layout);
        addToQueue(root, checker.getCurrentCode(), checker.getSymmetricCode());

        while (processingIndex < searchAmount) {
// System.out.println("searchAmount: " + searchAmount);
            HrdNode answer = curNode.giveBirth(checker, new Solver.IListener() {
                @Override
                public void validNodeCreated(HrdNode child) {
                    addToQueue(child, checker.getCurrentCode(), checker.getSymmetricCode());
                }
            });
            if (answer != null) {
// cost = System.currentTimeMillis() - t1;
// System.out.println("found answer! total steps: " + answer.level + " cost: " + cost + "ms");
// displaySolution(answer);
                root.outable = true;
            }
            processingIndex++;
            curNode = queue[processingIndex];
        }
        cost = System.currentTimeMillis() - t1;
        root.nodeNum = searchAmount;
        return root;
    }

    private void init(int hCount) {
// for (int m = 0; m < 6; m++) {
            LayoutEnumerator enumerator = new LayoutEnumerator(hCount);
            int count = 0;
            System.out.print(enumerator.getPrintLayout(enumerator.getLayout(), count));

            int[] first = enumerator.getBin15Layouts()[0];
            HashLayoutEncoder encoder = new HashLayoutEncoder(first);
            int[] JD = new int[encoder.bmTotal];   //節點字典
            codeSnMap = new int[encoder.bmTotal];

            while ((enumerator != null)) {
                int[][] layouts = enumerator.getBin15Layouts();
                for (int i = 0; i < layouts.length; i++) {
                    int code = encoder.encode(layouts[i]);
                    if (JD

== 0) {
JD

= 1;
codeSnMap

= count;
layoutMap[count] = layouts[i];
} else {
System.out.println("error! detected duplicate layout, count: " + count +
", code: " + code);
}
count++;
}
enumerator = enumerator.next();
}
System.out.println(String.format("%d橫式共有%d節點", hCount, count));
flagMap = new int[count];
// } //for
}

public void test(int hCount) {
init(hCount);
int solvableCount = 0;
int playableCount = 0;
ArrayList<HrdNode> trees = new ArrayList<>();
int[] curLayout = getUnSearchedLayout();
while (curLayout != null) {
HrdNode tree = find(curLayout);
trees.add(tree);
// System.out.println("found tree: " + tree.nodeNum + ", remain: " + getUnsearchedCount()
// + ", solvable: " + tree.outable);
if (tree.outable) {
solvableCount++;
if (tree.nodeNum > 500) {
playableCount++;
}
// System.out.print(LayoutEnumerator.getPrintLayout2(tree.layout, trees.size()));
// System.out.println("found tree: " + tree.nodeNum + ", remain: " + getUnsearchedCount());
}
curLayout = getUnSearchedLayout();
}
System.out.println(String.format("%d橫式共有%d顆樹, 其中%d個樹有解, %d個樹可玩性好",
hCount, trees.size(), solvableCount, playableCount));
}

private void markSearched(int code) {
if (code >= 0) {
int serialNum = codeSnMap

;
flagMap[serialNum] = 1;
}
}

private int[] getUnSearchedLayout() {
for (int i = 0; i < flagMap.length; i++ ) {
if (flagMap[i] != 1) {
return layoutMap[i];
}
}
return null;
}

private int getUnsearchedCount() {
int count = 0;
for (int i = 0; i < flagMap.length; i++ ) {
if (flagMap[i] != 1) {
count++;
}
}
return count;
}
}

package game.hrd.game.hrd.refactor;

import java.util.ArrayList;

/** * Created by huangcongjie on 2017/12/20. */
public class Solver {
    HrdNode[] queue;
    int searchAmount = 0;
    int processingIndex = 0;
    int maxAmount = 500000;

    private void addToQueue(HrdNode node) {
        if (searchAmount >= maxAmount) {
            System.out.println("對溢出");
            return;
        }
        queue[searchAmount] = node;
        searchAmount++;
    }

    //--廣度優先--
    public int bfs(int[] layout, boolean useHash) {
// System.out.println("use hash checker: " + useHash);
        long t1 = System.currentTimeMillis();
        long cost = 0;
        IAnalyzer analyzer = new HashLayoutAnalyzer();
        HrdNode root = new HrdNode(layout, analyzer, useHash);
        IChecker checker = useHash ? new LayoutToHash() : new LayoutToCode(root.layout);
        if (useHash) {
            maxAmount = 40 * 1024;
        } else {
            maxAmount = ((LayoutToCode) checker).codeTotalNum / 10;
        }
        queue = new HrdNode[maxAmount];
        HrdNode curNode = root;
        addToQueue(root);

        while (processingIndex < searchAmount) {
// System.out.println("searchAmount: " + searchAmount);
            HrdNode answer = curNode.giveBirth(checker, new IListener() {
                @Override
                public void validNodeCreated(HrdNode child) {
                    addToQueue(child);
                }
            });
            if (answer != null) {
                cost = System.currentTimeMillis() - t1;
                System.out.println("found answer! total steps: " + answer.level + " cost: " + cost + "ms");
                if (checker instanceof LayoutToHash) {
                    System.out.println("hash conflicts: " + ((LayoutToHash) checker).cht );
                }
// displaySolution(answer);
                return 1;
            }
            processingIndex++;
            curNode = queue[processingIndex];
        }
        cost = System.currentTimeMillis() - t1;
        System.out.println("already searched all nodes, cannot found answer! cost: " + cost + "ms");
        return 0;
    }

    public int reachable(int[] layout1, int[] layout2) {
        long t1 = System.currentTimeMillis();
        long cost = 0;
        IAnalyzer analyzer = new HashLayoutAnalyzer();
        HrdNode root = new HrdNode(layout1, analyzer, false);
        LayoutToCode checker = new LayoutToCode(root.layout);
        int dstCode = checker.encode(layout2);
        root.setGoal(dstCode);
        maxAmount = checker.codeTotalNum / 10;
        queue = new HrdNode[maxAmount];
        HrdNode curNode = root;
        addToQueue(root);

        while (processingIndex < searchAmount) {
// System.out.println("searchAmount: " + searchAmount);
            HrdNode answer = curNode.giveBirth(checker, new IListener() {
                @Override
                public void validNodeCreated(HrdNode child) {
                    addToQueue(child);
                }
            });
            if (answer != null) {
                cost = System.currentTimeMillis() - t1;
                System.out.println("found answer! total steps: " + answer.level + " cost: " + cost + "ms");
                displaySolution(answer);
                return 1;
            }
            processingIndex++;
            curNode = queue[processingIndex];
        }
        cost = System.currentTimeMillis() - t1;
        System.out.println("already searched all nodes, cannot found answer! cost: " + cost + "ms");
        return 0;
    }

    public interface IListener {
        void validNodeCreated(HrdNode child);
    }

    private void displaySolution(HrdNode hrdObj) {
        ArrayList<HrdNode> list = new ArrayList<>();
        list.add(hrdObj);
        HrdNode node = hrdObj;
        while (node.parent != null) {
            list.add(node.parent);
            node = node.parent;
        }
        for (int i = list.size() - 1; i >= 0; i--) {
            displayStep(list.get(i));
        }
    }

    private void displayStep(HrdNode hrdObj) {
        System.out.println("第" + hrdObj.level + "步");
        int[] layout = hrdObj.isHashLayout() ? HrdUtil.convertToScreen(hrdObj.layout) :
                hrdObj.layout;
        for (int i = 0; i < layout.length; i += 4) {
            System.out.println(String.format("%d\t%d\t%d\t%d", layout[i], layout[i+1], layout[i+2], layout[i+3]));
        }
        System.out.println();
    }
}

測試代碼

public class TestHrdEnumerator {
    public static void main(String args[]) {
// testBin15Layouts();
// testPmHx();
// testEncoder();
// testLayoutEncoder();

        for (int i = 0; i < 6; i++) {
            TreeFinder treeFinder = new TreeFinder();
            treeFinder.test(i);
        }
// testReachable();
    }

    public static void testBin15Layouts() {
        int hCount = 1;
        LayoutEnumerator enumerator = new LayoutEnumerator(hCount);
        int count = 0;
        System.out.print(enumerator.getPrintLayout(enumerator.getLayout(), count));

        int[][] layouts = enumerator.getBin15Layouts();
        for (int i = 0; i < layouts.length; i++) {
            count++;
            System.out.print(LayoutEnumerator.getPrintLayout(layouts[i], count));
        }
    }

    public static void testPmHx() {
        int hCount = 1;
        LayoutEnumerator enumerator = new LayoutEnumerator(hCount);
        int count = 0;
        System.out.print(enumerator.getPrintLayout(enumerator.getLayout(), count));

        PmHx hx = new PmHx();
        while ((enumerator != null)) {
            int[][] layouts = enumerator.getBin15Layouts();
            for (int i = 0; i < layouts.length; i++) {
                count++;
                if ( hx.check(layouts[i]) != 1 ) {
                    System.out.println("error! detected duplicate layout, count: " + count);
                }
            }
            enumerator = enumerator.next();
        }
        System.out.println(String.format("%d橫式共有%d節點, 發生哈希衝突%d", hCount, count, hx.cht));
    }

    public static void testEncoder() {
// int[] qp = {
// 6, 15, 15, 7,
// 6, 15, 15, 7,
// 8, 11, 11, 5,
// 8, 3, 4, 5,
// 2, 0, 0, 1
// };
// int[] qp = {
// 6, 15, 15, 7,
// 6, 15, 15, 7,
// 3, 11, 11, 5,
// 4, 10, 10, 5,
// 2, 0, 0, 1
// };
// int[] qp = {
// 10, 10, 5, 7,
// 11, 11, 5, 7,
// 12, 12, 3, 4,
// 15, 15, 1, 2,
// 15, 15, 0, 0
// };
        int[] qp = {
                8, 6, 5, 7,
                8, 6, 5, 7,
                1, 2, 3, 9,
                15, 15, 4, 9,
                15, 15, 0, 0
        };
        PmBm pmBm = new PmBm(qp);
// int code1 = pmBm.Bm(qp);
        int[] qp2 = HrdUtil.convertToHashLayout(qp);
        HashLayoutEncoder encoder = new HashLayoutEncoder(qp2);
// int code2 = encoder.encode(qp2);
// System.out.println(code1 + " == " +code2);

        int[] qp3 = {
                5, 5, 3, 3,
                1, 1, 1, 1,
                3, 3, 2, 2,
                1, 1, 2, 3,
                2, 0, 0, 1};
        int[] qp4 = {
                5, 5, 3, 3,
                1, 1, 1, 1,
                3, 2, 3, 2,
                1, 2, 1, 3,
                2, 0, 0, 1};
        int[] n1 = HrdUtil.hashLayoutConvertToNormal(qp3);
        int code4 = pmBm.Bm(n1);
        int code3 = encoder.encode(qp3);
        int code2 = encoder.encode(qp4);
        System.out.println(code3 + " != " +code2 + ", " + code4 + " == " + code3);
    }

    public static void testLayoutEncoder() {
        for (int m = 0; m < 6; m++) {
            int hCount = m;
            LayoutEnumerator enumerator = new LayoutEnumerator(hCount);
            int count = 0;
            System.out.print(enumerator.getPrintLayout(enumerator.getLayout(), count));

            int[] first = enumerator.getBin15Layouts()[0];
            HashLayoutEncoder encoder = new HashLayoutEncoder(first);
            PmBm pmBm = new PmBm(HrdUtil.hashLayoutConvertToNormal(first));
            int[] JD = new int[encoder.bmTotal];   //節點字典

            while ((enumerator != null)) {
                int[][] layouts = enumerator.getBin15Layouts();
                for (int i = 0; i < layouts.length; i++) {
                    count++;
                    int code = encoder.encode(layouts[i]);
// int[] normalLayout = HrdUtil.hashLayoutConvertToNormal(layouts[i]);
// int code = pmBm.Bm(normalLayout);
                    if (JD

== 0) {
JD

= 1;
} else {
System.out.println("error! detected duplicate layout, count: " + count +
", code: " + code);
}
}
enumerator = enumerator.next();
}
System.out.println(String.format("%d橫式共有%d節點", hCount, count));
}
}

static public String toLayoutString(int[] layout2) {
String layout = "";
for (int i = 0; i < 20; i++) {
layout += layout2[i];
if (i % 4 == 3) {
layout += ",|";
} else {
layout += ", ";
}
}
layout = layout.substring(0, layout.length() - 2);
return layout;
}

static public void testReachable() {
int[] qp1 = {
5, 5, 4, 4,
1, 1, 3, 3,
3, 3, 1, 1,
1, 1, 2, 2,
2, 2, 0, 0
};
int[] qp2 = {
5, 5, 3, 3,
1, 1, 1, 1,
4, 4, 3, 3,
2, 2, 1, 1,
2, 2, 0, 0
};
Solver solver = new Solver();
solver.reachable(qp1, qp2);
}
}

最終結果,

5   5   3   3   
1   1   1   1   
3   3   3   0   
1   1   1   0   
0   0   0   0   
---------0-----------

0橫式共有15660節點
0橫式共有80顆樹, 其中1個樹有解, 1個樹可玩性好
5   5   4   4   
1   1   3   3   
3   3   1   1   
1   1   0   0   
0   0   0   0   
---------0-----------

1橫式共有65880節點
1橫式共有898顆樹, 其中52個樹有解, 2個樹可玩性好
5   5   4   4   
1   1   4   4   
3   3   3   0   
1   1   1   0   
0   0   0   0   
---------0-----------

2橫式共有109260節點
2橫式共有2653顆樹, 其中230個樹有解, 1個樹可玩性好
5   5   4   4   
1   1   4   4   
4   4   3   3   
0   0   1   1   
0   0   0   0   
---------0-----------

3橫式共有106800節點
3橫式共有2609顆樹, 其中146個樹有解, 1個樹可玩性好
5   5   4   4   
1   1   4   4   
4   4   4   4   
3   0   0   0   
1   0   0   0   
---------0-----------

4橫式共有51660節點
4橫式共有1729顆樹, 其中107個樹有解, 1個樹可玩性好
5   5   4   4   
1   1   4   4   
4   4   4   4   
4   4   0   0   
0   0   0   0   
---------0-----------

5橫式共有14220節點
5橫式共有505顆樹, 其中19個樹有解, 1個樹可玩性好

“可玩性好”表示樹的節點不能少於500個,不然就太簡單了。

對於任意一顆華容道的樹,如何找到一種節點佈局,使得以此節點爲根,生成的樹,層數最少?
如何找到另有一種節點,使得以此節點爲根,生成的樹,層數最多?
這兩個問題是新的坑,太閒了再來研究,

聯繫郵箱: [email protected]

点赞