1. 程式人生 > >遞迴、回溯-圖的m著色問題

遞迴、回溯-圖的m著色問題

1、問題描述

給定無向連通圖G=(V,E)和m種不同的顏色。用這些顏色為圖G的各頂點著色,每個頂點著一種顏色。
是否有一種著色法使G中每條邊的2個頂點著不同顏色。這個問題是圖的m可著色判定問題。

輸入:圖的頂點的個數,顏色種類樹m。輸出頂點a與頂點b,表示a與b之間有一條邊。

輸出:列印所有方案,輸出頂點Ai(1<=i<=n)的顏色種類編號,可著色的方案總數。

執行結果:

2、演算法設計

本節討論給定連通圖G=(V,E)和m種顏色,如果該圖不是m可著色,給出否定回答;若m可著色,找出所有不同著色方法。
約束條件:頂點k與已著色的相鄰結點顏色不重複

下面根據回溯法的遞迴描述框架Backtrack設計圖的m著色演算法,用圖的鄰接矩陣a表示無向連通圖G= (V,E)。若(i,j)屬於圖G=(V,E)的邊集E,則a[i][j] = 1,否則a[i][j]=0,整數 1,2,....m用來表示m種不同顏色。頂點i所著的顏色用x[i]表示。陣列x[1:n]是問題的解向量。問題的解空間可表示為一顆高度為n+1的完全m叉樹,解空間樹的第i(1<=i<=n)層中每一結點都有m個兒子,每個兒子相應於x[i]的m個可能的著色之一。第n+1層結點均為葉結點。

在下面的解圖的m可著色問題的回溯法中。Backtrack(i)搜尋解空間中第i層子樹。類Color的資料成員記錄解空間中結點資訊,以減少傳給Backtrack的引數。sum記錄當前已找到的可m著色方案數。

在演算法Backtrack中,當i>n時,演算法搜尋至葉結點,得到新的m著色方案,當前找到的可m著色方案數sum增1.

當i<=n時,當前擴充套件結點Z是解空間中的內部結點,該結點有x[i] = 1,2,....m共m個兒子結點。對當前擴充套件結點Z的每一個兒子結點,由函式OK檢查其可行性,並以深度優先的方式遞迴的對可行子樹進行搜尋,或剪去不可行子樹。

class Color
{
    friend int mColoring(int, int, int **);
private:
    void BackTrace(int t);
    bool Ok(int k);

    int n,                                  //圖的頂點數
        m,                                  //可用顏色數
        **a,                                //a表示圖的鄰接矩陣,a[k][i]表示第k個頂點和第i個頂點有邊相連
        *x,                                 //x[k]表示連通圖中第k個結點的顏色,x[i]表示連通圖中第i個頂點的顏色
        sum;                                //當前已找到的可m著色方案數量
};
//判斷頂點k的著色是否與前面已著色的k-1個頂點發生衝突
bool Color::Ok(int k)
{
    int i;

    for(i = 1; i < k; i++)
        if(a[k][i] == 1 && x[i] == x[k])    //頂點相連 頂點同色
            return false;
    return true;
}
//對解空間樹回溯搜尋,求得可行著色方案數量
void Color::BackTrace(int t)
{
    int i;
    if(t > n)                               //演算法搜尋到葉節點,得到一個新的每條邊的兩個頂點著不同顏色的m著色方案
    {
        sum++;                              //當前已找到的著色方案數sum+1
        for(i = 1; i <= n; i++)             //輸出找到的當前著色方案
            printf("%d ", x[i]);
        printf("\n");
        return;
    }

    for(i = 1; i <= m; i++)                 //該結點有x[i]=1,2..m個兒子結點,表示第k個頂點可著m種顏色,
    {                                       //搜尋當前擴充套件結點的每一個兒子結點
        x[t] = i;
        if(Ok(t))                           //檢查可行性
            BackTrace(t+1);                 //滿足約束函式,已深度優先的方式對子樹搜尋,不滿足就剪去以該結點為根的子樹
        x[t] = 0;
    }
}

//負責對變數初始化,呼叫遞迴函式,BackTrack(1)實現回溯搜尋並返回可行m著色方案數量
int mColoring(int n, int m, int **a)
{
    Color X;
    int *p, i;

    p = new int[n+1];
    for(i = 1; i <= n; i++)
        p[i] = 0;
    X.n = n;
    X.m = m;
    X.a = a;
    X.x = p;
    X.sum = 0;
    X.BackTrace(1);                 //求得可行m著色方案數量
    delete []p;

    return X.sum;                   //返回可行m著色方案數量
}

3、演算法效率

圖m可著色問題的回溯演算法的計算時間上界可以通過計算解空間樹中內結點個數來估計。圖m可著色問題的解空間樹中內結點個數是\sum_{i=0}^{n-1}m^{i}。對於每一個內結點,在最壞情況下,用OK檢查當前擴充套件結點的每一個兒子所相應的顏色的可用性需耗時O(mn)。因此,回溯法總的時間耗費是 \sum_{i=0}^{n-1}m^{i}(mn) = nm(m^{n}-1)/(m-1)=O(nm^{n})