回溯法之圖的着色問題


問題描述:
圖着色問題(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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM