回溯算法 --- 例題7.圖的m着色問題


一.問題描述

給定無向連通圖G和m種不同的顏色.用這些顏色為圖G的各項點着色,每個項點畫一種顏色.是否有一種着色法,使G中每條邊的2個頂點有着不同顏色?

二.解題思路

圖的m色判定問題:
給定無向連通圖G和m種顏色。用這些顏色為圖G的各頂點着色. 問是否存在着色方法, 使得G中任2鄰接點有不同顏色。

圖的m色優化問題:
給定無向連通圖G,為圖G的各頂點着色, 使圖中任2鄰接點着不同顏色,問最少需要幾種顏色。所需的最少顏色的數目m稱為該圖的色數

若圖G是可平面圖,則它的色數不超過4色(4色定理).
4色定理的應用:在一個平面或球面上的任何地圖能夠只用4種顏色來着色使得相鄰的國家在地圖上着有不同顏色

例如:

[這里有一個適用於任意圖着色的Welch Powell法,感興趣的同學可以看看.](#Welch Powell)

回到該問題,我們可以很清晰地看出問題的解空間樹是一棵排列樹,因為我們確定n個元素滿足某種性質的排列,這個性質就是一條邊的兩個點顏色不相同.而不是說找到n個元素的一個子集,這是要做的第一步.

具體的算法實現上:
設圖G=(V, E), |V|=n, 顏色數= m, 用鄰接矩陣a表示G, 用整數1, 2…m來表示m種不同的顏色。頂點i所着的顏色用x[i]表示。

問題的解向量可以表示為n元組x={ x[1],...,x[n] }. x[i]Î{1,2,...,m},解空間樹為排序樹,是一棵n+1層的完全m叉樹.在解空間樹中做深度優先搜索, 約束條件: x[i] ≠ x[j], 如果a[j].[i] = 1.

代碼如下:

// 圖的m着色問題
#include<bits/stdc++.h>
using namespace std;
class Color
{
    friend int mColoring(int, int, int **);
    private:
        bool CanDraw(int k);
        void Backtrack(int i);
        int n,          //圖的頂點數
            m,          //可用顏色數
            **a,        //圖的鄰接矩陣
            *x;         //當前解
        long sum;       //當前已經找到的可m着色方案數
};
bool Color::CanDraw(int i)      //檢查第i層填寫的顏色x[i]是否可用
{
    for(int j=1; j<i; j++)
    {
        if(a[i][j]==1 && x[j]==x[i])
            return false;
    }
    return true;
}
void Color::Backtrack(int i)
{
    if(i > n)
    {
        sum++;
        cout<<"第"<<sum<<"個解:";
        for(int i=1; i<=n; i++)
            cout<<x[i]<<" ";
        cout<<endl;
        return;
    }
    for(int k=1; k<=m; k++)  //依次從m種顏色中選擇,由於每一個分支的處理方法一樣,所以直接用一個循環,而不需要分別寫
    {
        x[i] = k;
        if(CanDraw(i))  
        {
            cout<<"顏色"<<k<<"可行,深入一層,將到達"<<i+1<<"層"<<endl;
            Backtrack(i+1);
            cout<<"回溯一層到達第"<<i<<"層"<<endl;
        }
        else 
        {
            if(k==m) cout<<"當前層所有顏色選完,沒有可行顏色,故將回溯一層到達第"<<i-1<<"層"<<endl;
            else cout<<"顏色"<<k<<"不可行,繼續選擇顏色"<<k+1<<endl;
        }
        x[i] = 0;
    }
}
int mColoring(int n, int m, int **a)
{
    Color X;
    // 初始化X
    X.n = n;
    X.m = m;
    X.a = a;
    X.sum = 0;
    int *p = new int[n+1];
    for(int i=0; i<=n; i++) p[i] = 0;
    X.x = p;
    X.Backtrack(1);
    delete[] p;
    return X.sum;
}
int main()
{
    cout<<"請輸入頂點個數和顏色種數:";
    int n, m;
    while(cin>>n>>m && n && m)
    {
        cout<<"請輸入鄰接矩陣"<<endl;
        int **a = new int*[n+1];
        for(int i=0; i<=n; i++) a[i] = new int[n+1];
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                cin>>a[i][j];
        int ans = mColoring(n, m, a);
        cout<<"圖的"<<m<<"着色方案共有"<<ans<<"種"<<endl;
        for(int i=0; i<=n; i++) delete[] a[i];
        delete[] a;
        cout<<"請輸入頂點個數和顏色種數:";
    }
    system("pause");
    return 0;
}

運行結果:

由此可以結合排列樹看一看,十分清晰明了.

參考畢方明老師《算法設計與分析》課件.

歡迎大家訪問個人博客網站---喬治的編程小屋,和我一起加油吧!


 Welch Powell方法 

針對任意圖着色問題,Welch Powell方法:

  • 將G的結點按照度數遞減的次序排列
  • 用第一種顏色對第一個結點着色,並按照結點排列的次序對與前面着色點不鄰接的每一點着以相同顏色
  • 用第二種顏色對尚未着色的點重復步驟2,用第三種顏色繼續這種作法,直到所有點着色完為止

例如:


免責聲明!

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



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