1. 程式人生 > 實用技巧 >回溯法之圖的著色問題

回溯法之圖的著色問題

問題描述:
圖著色問題(Graph Coloring Problem, GCP) 又稱著色問題,是最著名的NP-完全問題之一。
數學定義:給定一個無向圖G=(V, E),其中V為頂點集合,E為邊集合,圖著色問題即為將V分為K個顏色組,每個組形成一個獨立集,即其中沒有相鄰的頂點。其優化版本是希望獲得最小的K值。

圖的m-著色判定問題——給定無向連通圖G和m種不同的顏色。用這些顏色為圖G的各頂點著色,每個頂點著一種顏色,是否有一種著色法使G中任意相鄰的2個頂點著不同顏色?

抽象為圖結構

演算法流程

問題簡單分析:

這個問題和八皇后還有求子集和等問題都具有類似之處,其核心在通過遍歷找到所有的問題子集 ,但是在遞迴遍歷的時候,都在加一個判斷,將那些明顯不滿足條件的情況給直接排出,減少問題的規模,其實這類問題,在遞迴遍歷的時候都是類似與對一顆樹的便利每個節點相當走到此時的狀態,然後再判斷此時的狀態是否能繼續走下去,如果不能就將其回溯到上一個節點,避免浪費時間。

下面以一個簡單例子作分析:

分析如下:

時間複雜度分析:

程式碼實現如下:

import java.util.Scanner;

public class Main {
    static int[][] e = new int[5][5]; //儲存各個邊的情況連同為1 不連為0
    static int[] state = new int[e.length]; //表示當前染色情況
    static int Colornum = 3;//共有幾種顏色

    static void sear(int index) {//遞迴函式
        if (isOk(index)) {//判斷當前狀態能否滿足條件
if (index == e.length - 1) {//base case 若已經染到最後一個節點則輸出情況 Show(index); } else { for (int i = 1; i <= Colornum; i++) {//將所有 的顏色情況給遍歷 state[index + 1] = i;//假如下一個染色, sear(index + 1);//進入下次遞歸併且在遞迴的入口判斷是否滿足條件 } } } }
//列印當前狀態 private static void Show(int index) { for (int i = 1; i <= index; i++) { System.out.println(i + "is " + "Color " + state[i]); } System.out.println(); } //判斷是否能染色 private static boolean isOk(int index) { for (int i = 1; i < index; i++) { if (e[index][i] == 1 && state[i] == state[index])//當兩個節點是連同並且顏色一樣則不滿足返回false return false; } return true; } public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); //輸入邊的情況 for (int i = 1; i <= n; i++) { int a = in.nextInt(); int b = in.nextInt(); e[a][b] = 1; e[b][a] = 1; } //從0開始遞迴,但0不是一個節點 sear(0); } }
View Code

實現結果如下:

著色問題的應用:

問題描述:

n個人參加某項特殊考試。   為了公平,要求任何兩個認識的人不能分在同一個考場。求是少需要分幾個考場才能滿足條件。
輸入格式   
第一行,一個整數n(1 < n < 100),表示參加考試的人數。  
第二行,一個整數m,表示接下來有m行資料
以下m行每行的格式為:兩個整數a,b,用空格分開 (1<=a,b<=n) 表示第a個人與第b個人認識。 輸出格式
一行一個整數,表示最少分幾個考場。

程式碼實現:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;

public class Main {

    static ArrayList<ArrayList<Integer>> list = null;
    static HashSet<Integer>[] map;

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int m = input.nextInt();

        map = new HashSet[n + 1];

        for (int i = 0; i < m; i++) {
            int key = input.nextInt();
            int value = input.nextInt();

            if (map[key] == null) {
                map[key] = new HashSet<Integer>();
            }

            if (map[value] == null) {
                map[value] = new HashSet<Integer>();
            }
            map[key].add(value);
            map[value].add(key);
        }

        list = new ArrayList<ArrayList<Integer>>();

        for (int i = 1; i <= n; i++) {

            if (list.size() == 0) {
                ArrayList<Integer> child = new ArrayList<Integer>();
                child.add(i);
                list.add(child);
            } else {
                boolean zhaodaomei = false;
                for (int j = 0; j < list.size(); j++) {
                    if (isBo(j, i)) {
                        list.get(j).add(i);
                        zhaodaomei = true;
                        break;
                    }
                }
                if (zhaodaomei == false) {// 沒找到哦
                    ArrayList<Integer> child = new ArrayList<Integer>();
                    child.add(i);
                    list.add(child);
                }

            }

        }

        System.out.println(list.size());

    }
    public static boolean isBo(int school, int num) {
        ArrayList<Integer> child = list.get(school);
        HashSet<Integer> set = map[num];

        if (set == null)
            return true;

        for (int i = 0; i < child.size(); i++) {
            Integer stu = child.get(i);
            if (set.contains(stu)) {
                return false;
            }
        }
        return true;
    }

}
View Code

實現結果:

參考文獻:北大《演算法設計與分析》公開課

CSDN:https://blog.csdn.net/wdays83892469/article/details/79648258