算法:根據四色定理(Four color theorem),求出地圖的所有着色方案

地圖着色,需要每一個區域都使用一種顏色來進行填充,然後爲了與相鄰接壤的區域分開,就要求兩個接壤的區域需要使用不同的顏色。四色定理的意思是,最多只需要四種顏色,就可以爲所有的地圖進行全部區域着色,且任意兩個接壤的區域都是不同的顏色。在四色定理的指導下,我用Java來實現求解任意一副地圖的所有可行的區域着色方案的算法,並求解下面這幅示例地圖的所有着色方案:
在這裏插入圖片描述
算法思路:

  1. 將所有的區域進行編號,從0開始,然後用一個長度爲區域總數的數組記錄,稱爲顏色方案數組
  2. 顏色方案數組的下標是區域編號,值是使用的顏色,代號爲1、2、3、4四種顏色。初始化全部是0,也就是顏色0代表未着色,如果大於0,代表已着色,值就是顏色
  3. 創建無權無向圖,所有區域映射圖的頂點,如果兩個區域是接壤的,則對應圖的兩個頂點之間就是連通的,然後求出該無權無向圖的記錄有地圖區域接壤關係的鄰接矩陣
  4. 0區域默認使用1號顏色,然後找到下一個區域,根據接壤關係的鄰接矩陣和顏色方案數組,找到下一個區域的所有接壤區域已經使用過的顏色集,再反向求出可使用的顏色集
  5. 如果下一個區域的可使用顏色集爲空集,則當前遞歸分支結束。如果不爲空集,則遍歷所有可用顏色,然後遞歸調用,一個可用顏色爲一個遞歸分支,着色下一個區域……
  6. 遞歸方法在着色後進行判斷,如果剛剛着了色的區域已經是最後一個區域了,則表示程序已經找到了地圖着色方案。然後程序輸出顏色方案數組,以展示可行的地圖着色方案

第一步:將所有的區域進行編號,從0開始
在這裏插入圖片描述
第二步:創建無權無向圖,所有區域映射圖的頂點,如果兩個區域是接壤的,則連通對應圖的兩個頂點
在這裏插入圖片描述
3、根據上面記錄有地圖區域接壤關係的無權無向圖,求出該圖的鄰接矩陣。該鄰接矩陣也是算法輸入的數據來源
在這裏插入圖片描述
下面是我用Java實現的求解上面示例圖的着色方案的算法。該算法的思想和精髓已經在下面的代碼和其間的詳盡註釋中:

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @author LiYang
 * @ClassName FourColorTheorem
 * @Description 四色定理探索實踐類
 * @date 2019/12/7 22:32
 */
public class FourColorTheorem {

    //根據四色定理,最多隻使用四種顏色,就可以爲任何地圖
    //着色,並且保證相鄰接壤的區域必須用不同的顏色
    //---------------------------------------------
    //注意,如果你的地圖足夠特別,也是有可能三種,甚至更少
    //的顏色種類就完成着色,此時可以將下面的數字改小,然後
    //查看控制檯是否有結果輸出,有結果輸出則表示可行
    private static final int MIN_NECESSARY_MAP_COLOR = 4;

    /**
     * 返回示例地圖接壤信息的鄰接矩陣
     * @return 示例地圖接壤信息的鄰接矩陣
     */
    public static int[][] initMapMatrix() {
        //直接返回記錄示例地圖接壤信息的鄰接矩陣
        //要運行自己的地圖,可以修改此鄰接矩陣
        return new int[][] {
            {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0},
            {1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0},
            {0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0},
            {0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1},
            {0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0},
            {0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0},
            {0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1},
            {0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1},
            {0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0}
        };
    }

    /**
     * 深拷貝數組
     * @param source 數組拷貝源
     * @return 數組副本
     */
    public static int[] copyArray(int[] source) {
        //創建新的數組
        int[] copy = new int[source.length];

        //拷貝賦值新數組
        System.arraycopy(source, 0, copy, 0, source.length);

        //返回深拷貝的副本
        return copy;
    }

    /**
     * 地圖區域着色算法的遞歸執行方法
     * @param nextArea 着色區域
     * @param color 着色使用的顏色
     * @param colorPlan 着色計劃記錄數組
     * @param matrix 區域接壤的鄰接矩陣
     */
    public static void coloringMap(int nextArea, int color, int[] colorPlan, int[][] matrix) {
        //將當前區域着色
        colorPlan[nextArea] = color;

        //如果已經全部着色,則打印結果
        if (nextArea == colorPlan.length - 1) {

            //打印當前遞歸分支的着色方案
            System.out.println("找到着色方案:" + Arrays.toString(colorPlan));

            //結束當前遞歸分支
            return;
        }

        //當前區域更新爲下一個區域
        //準備爲下一個區域進行着色
        nextArea ++;

        //下一個區域的可用顏色集
        Set<Integer> availableColor = new HashSet<>();

        //初始化可用顏色集
        for (int i = 1; i <= MIN_NECESSARY_MAP_COLOR; i++) {

            //先全部加入,再用排除法去掉周邊接壤顏色
            availableColor.add(i);
        }

        //遍歷鄰接矩陣,找到下一個區域的所有接壤區域
        for (int i = 0; i < matrix.length; i++) {

            //如果當前區域接壤,且已經着色
            if (matrix[nextArea][i] > 0 && colorPlan[i] > 0) {

                //將接壤的已用顏色剔除
                availableColor.remove(colorPlan[i]);
            }
        }

        //遍歷下一個區域的所有可用顏色
        for (int available : availableColor) {

            //分別遞歸調用本算法,嘗試下一個區域着所有可用顏色集
            coloringMap(nextArea, available, copyArray(colorPlan), matrix);
        }
    }

    /**
     * 地圖區域着色算法的驅動方法
     * @param matrix 地圖接壤的鄰接矩陣
     */
    public static void coloringMap(int[][] matrix) {
        //我們從0號區域開始,從哪個區域開始都一樣
        int nextArea = 0;

        //顏色代號是1-4,本質是等價可互換的,用1開始就行
        int color = 1;

        //初始化着色方案
        int[] colorPlan = new int[matrix.length];

        //調用地圖區域着色算法的遞歸執行方法
        coloringMap(nextArea, color, colorPlan, matrix);
    }

    /**
     * 運用四色定理,運行示例地圖着色的算法
     * @param args
     */
    public static void main(String[] args) {
        //獲得記錄示例地圖接壤關係的鄰接矩陣
        int[][] matrix = initMapMatrix();

        //運行示例地圖着色的算法,並從控制檯
        //查看打印的着色方案
        coloringMap(matrix);
    }

}

運行FourColorTheorem類的main方法,控制檯輸出了很多可行的示例圖着色方案!下面是我隨意摘選的一部分示例圖着色方案:

找到着色方案:[1, 2, 2, 3, 1, 1, 2, 3, 4, 2, 3]
找到着色方案:[1, 3, 2, 4, 2, 1, 3, 4, 2, 3, 4]
找到着色方案:[1, 4, 2, 4, 2, 1, 4, 3, 2, 1, 4]
找到着色方案:[1, 4, 4, 3, 3, 2, 4, 1, 3, 2, 1]

接下來我們驗證算法求出的示例圖着色方案。1號我們用紅色,2號用黃色,3號用藍色,4號用粉色,驗證上面我隨意篩選的四種示例圖着色方案,地圖着色後表明,該算法測試通過:
在這裏插入圖片描述

發佈了67 篇原創文章 · 獲贊 14 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章