圖的m着色問題算法
一:問題描述數組
給定無向連通圖G=(V,E)和m種不一樣的顏色。用這些顏色爲圖G的各頂點着色,每一個頂點着一種顏色。若是有一種着色法使G中有邊相連的兩個頂點着不一樣的顏色,則稱這個圖是m可着色的。圖的m着色問題是對於給定圖G和m中顏色,找出全部不一樣的着色方法。數據結構
二:問題分析框架
該問題中每一個頂點所着的顏色均有m中選擇,n個頂點所着顏色的一個組合是一個可能的解。根據回溯法的算法框架,定義問題的解空間及其組織結構是很容易的。從給定的已知條件看,無向連通圖G中假設有n個頂點,它確定至少有n-1條邊,有邊相連的兩個頂點所着顏色不相同,n個頂點所着顏色的全部組合中必然存在不是問題着色方案的組合,所以須要設置約束條件;而針對全部可行解(組合),不存在可行解的優劣問題。因此不須要設置限界條件。函數
三:算法設計spa
I.定義數據結構設計
定義問題的解空間爲數組x[]code
‚ 定義無向連通圖的存儲空間和形式,爲二維數組a[][]blog
ƒ定義數組b[3],b[0]存儲無向連通圖的節點個數,b[1]存儲無向連通圖的邊數,b[2]存放着色方案的個數。io
II.肯定解空間的組織結構
問題的解空間組織結構是一顆滿m叉樹,樹的深度爲n(n指連通圖結點數)
III.搜索解空間
IV.設置約束條件
題中條件爲,當前頂點要與前面已經肯定顏色且有邊相連的頂點的顏色不相同。假設當前擴展結點所在的層次爲t,則下一步擴展就是要判斷第t個頂點着什麼顏色,第t個頂點所着的顏色要與已經肯定的第1-(t-1)個頂點中與其有邊相連的顏色不相同。
因此,約束函數可描述爲:
int ok(int a[M][M],int x[M],int t) { for(int j=1;j<t;j++) if(a[t][j]) { if(x[j]==x[t]) return 0; } return 1; }
V.無需設置限界條件
VI.搜索過程。拓展結點沿着某個分支拓展時須要判斷約束條件,若是知足,則進入深一層次繼續搜索;若是不知足,則拓展生成的結點被剪掉。搜索到葉子結點時,找到一種着色方案。搜索過程直到全部活結點變成死結點爲止。
四:實例構造
以下圖所示的無向連通圖和m=3.
搜索過程:
從根結點A開始,結點A是當前的活結點,也是當前的拓展節點,太表明的狀態是給定的無向連通圖中任何一個頂點都尚未着色。沿着x[1]=1分支拓展,知足約束條件,生成的結點B將成爲活結點,而且成爲當前的拓展結點。拓展結點B沿着x[2]=1拓展,不知足約束條件,生成的結點被剪掉,而後沿着x[2]=2,分支拓展,知足約束條件,生成的結點C成爲活結點,而且成爲當前的拓展結點.....等等,其餘搜索依次進行,由下述程序可得最終結果,有6中着色方案,即(1 2 3 1 3)、(1 3 2 1 2)、(2 1 3 2 3)、(2 3 1 2 1)、(3 1 2 3 2)、(3 2 1 3 1)。
C/C++程序以下:
1 #include<stdio.h> 2 #define M 30 3 //圖的m着色問題 4 void print(int a[M][M],int b[3]) 5 { 6 int q=0,p=0,i=0,j=0,c=0,d=0; 7 //輸入 8 printf("請輸入無向連通圖的節點數:\n"); 9 scanf("%d",&q); 10 b[0]=q; 11 printf("請輸入無向連通圖的邊數:\n"); 12 scanf("%d",&p); 13 b[1]=p; 14 for(i=0;i<p;i++) 15 { 16 printf("第%d條邊的起點 終點:",i+1); 17 scanf("%d%d",&c,&d); 18 a[c][d]=1; 19 a[d][c]=1; 20 } 21 //輸出 22 printf("無向連通圖矩陣形式:\n"); 23 for(i=0;i<=q;i++) 24 { 25 for(j=0;j<=q;j++) 26 { 27 printf("%4d",a[i][j]); 28 } 29 printf("\n"); 30 } 31 } 32 33 int ok(int a[M][M],int x[M],int t) 34 { 35 for(int j=1;j<t;j++) 36 if(a[t][j]) 37 { if(x[j]==x[t]) 38 39 return 0; 40 } 41 return 1; 42 } 43 44 void Backtrack(int t,int x[],int m,int b[3],int a[M][M]) //m種顏色 45 { 46 47 if(t>b[0]) 48 { 49 b[2]++; 50 printf("第%d種着色方案:\n",b[2]); 51 for(int i=1;i<=b[0];i++) 52 { 53 printf("%4d",x[i]); 54 } 55 printf("\n"); 56 } 57 else 58 { 59 for(int i=1;i<=m;i++) 60 { 61 x[t]=i; 62 if(ok(a,x,t)!=0) 63 Backtrack(t+1,x,m,b,a); 64 } 65 } 66 } 67 68 void main() 69 { 70 int a[M][M]={0}; 71 int x[M]; //解空間 72 int b[3]={0};//b[0]存放節點總數 b[1]存放邊的總數 73 int m=3; //m種顏色 74 print(a,b); 75 Backtrack(1,x,m,b,a); 76 77 }
//************************運行結果以下**********************//
/*
請輸入無向連通圖的節點數:
5
請輸入無向連通圖的邊數:
7
第1條邊的起點 終點:1 2
第2條邊的起點 終點:1 3
第3條邊的起點 終點:2 3
第4條邊的起點 終點:2 4
第5條邊的起點 終點:2 5
第6條邊的起點 終點:3 4
第7條邊的起點 終點:4 5
無向連通圖矩陣形式:
0 0 0 0 0 0
0 0 1 1 0 0
0 1 0 1 1 1
0 1 1 0 1 0
0 0 1 1 0 1
0 0 1 0 1 0
第1種着色方案:
1 2 3 1 3
第2種着色方案:
1 3 2 1 2
第3種着色方案:
2 1 3 2 3
第4種着色方案:
2 3 1 2 1
第5種着色方案:
3 1 2 3 2
第6種着色方案:
3 2 1 3 1
Press any key to continue*/
五:算法描述
在算法描述中,數組x[]記錄着色方案,b[]數組中元素b[2]記錄着色方案的種數,初始值爲0;m爲給定的顏色數;該算法的關鍵是判斷當前的結點能夠着那種顏色。圖的m着色問題的算法描述以下:
void Backtrack(int t,int x[],int m,int b[3],int a[M][M]) //m種顏色 { if(t>b[0]) { b[2]++; printf("第%d種着色方案:\n",b[2]); for(int i=1;i<=b[0];i++) { printf("%4d",x[i]); } printf("\n"); } else { for(int i=1;i<=m;i++) { x[t]=i; if(ok(a,x,t)!=0) Backtrack(t+1,x,m,b,a); } } }
從根結點開始搜索着色方案,即Backtrack(1,x,m,b,a)。
六:算法分析
計算限界函數須要O(n)時間,須要判斷限界函數的結點在最壞的狀況下有1+m+m^2+m^3+...+m^(n-1)=(m^n-1)/(m-1)個,故耗時O(n*m^n);在葉子結點處輸出着色方案須要耗時O(n),在最壞的狀況下會搜索到每個葉子節點,葉子節點有m^n個,故耗時爲O(n*m^n)。圖的m的着色問題的回溯算法所需的時間爲O(n*m^n)+O(n*m^n)=O(n*m^n)。
七:小結
1.當所給的問題的n個元素中每個元素均有m中選擇,要求肯定期中的一種選擇,使得對這n個元素的選擇結果向量知足某種性質,這類問題的解空間稱爲滿m叉樹。都可以用上述算法進行求解。
2.上述C/C++程序中也可改進爲將m的值由用戶輸入,而後存入b[4]數組中的元素b[3],以方便求m爲不一樣值時所需的着色狀況。