著名的四色定理說到,“如果在平面上划出一些鄰接的有限區域,那么可以用四種顏色來給這些區域染色,使得每兩個鄰接區域染的顏色都不一樣”
另一個通俗的說法是,“任意一個無飛地的地圖都可以用四種顏色染色,使得沒有兩個相鄰國家染的顏色相同。”
定理的證明比較復雜,但可以確信:四種顏色是足夠染完平面圖,並且保證每兩個鄰接區域染的顏色都不一樣。
在我的研究工作中,需要實現一個這樣的算法,最初我用DFS實現,確信思路正確,也測試了幾組數據,然而在區域數較多的時候,由於堆棧深度過深,導致程序崩潰(我猜想是這個原因),所以后來嘗試用了非遞歸實現的方法。
圖像處理中的四色標記問題可以定義為:
給定鄰接矩陣 Adj[n][n],其中若第i個區域與第j個區域相鄰,則A[i][j]=1,否則A[i][j]=0,求四色標記該區域的一組解(注意:解空間可能達到4^n,故只求一組解)
/* ************************************* * Main Function * Para: * @Adj: Adjacent Matrix * @Record: Record the labeled results * @num: The number of regions * * *************************************/ void FourColorLabel(int ** Adj, int * Record, int num) { // 染色第一個區域,先設置為1 Record[0]=1; int m=1, n=1; // m計數,n為顏色值 // 一直染色,直到染完 while (m<=num) { while(n<=4&& m<=num) { int k=0; for (k=0; k<m; k++) { if (Adj[m][k]==1 && Record[k]==n) break; // 染色有沖突 } if (k<m) n++; // 染色有沖突,換新顏色 else // 當前染色OK { Record[m]=n; m++; n=1; } } if (n>4) // 如果當前用的已經超出標記范圍(說明在已標記的情況下,目前區域無合適的顏色,必須回退) { m--; n=Record[m]+1; } } }
以上的復雜度其實不是很好估計,跟具體的圖有關,回退次數不多的情況下,很快就能完成所有染色,否則,需要更多的回退次數。
附上我之前用DFS染色的程序示例:
1 #include <iostream> 2 3 using namespace std; 4 5 /* ************************************* 6 * 7 * Four Color Label 8 * 9 * *************************************/ 10 11 /* ************************************* 12 * Main Function 13 * Para: 14 * @Adj: Adjacent Matrix 15 * @Record: Record the labeled results 16 * @cur: current index 17 * @n: The number of regions 18 * 19 * *************************************/ 20 void FourColorLabel(int Adj[][7], int* Record, int cur, int n); 21 22 /* ************************************* 23 * CheckOk 24 * Para: 25 * @Adj: Adjacent Matrix 26 * @Record: Record the labeled results 27 * @cur: current index 28 * @clabel: The label of cur 29 * 30 * Check "color the cur region with clabel" is ok or not 31 * *************************************/ 32 bool CheckOk(int Adj[][7], int *Record, int cur, int clabel); 33 34 int main() 35 { 36 37 38 /* **************************************************** 39 * 40 * Test CASE 41 * 42 * ---------------------- 43 * | 1 / 2 / | 44 * | /-------/ 3 | 45 * |---/ \-------| 46 * | 4 / | 47 * |------------/ 5 | 48 * | \------| 49 * | 6 / | 50 * | / 7 | 51 * ---------------------- 52 * ****************************************************/ 53 int Adj[7][7] = { {0, 1, 0, 1, 0, 0, 0}, 54 {1, 0, 1, 1, 0, 0, 0}, 55 {0, 1, 0, 1, 1, 0, 0}, 56 {1, 1, 1, 0, 1, 1, 0}, 57 {0, 0, 1, 1, 0, 1, 1}, 58 {0, 0, 0, 1, 1, 0, 1}, 59 {0, 0, 0, 0, 1, 1, 0} }; 60 61 62 int Record[7] = {0}; 63 64 FourColorLabel(Adj, Record, 0, 7); 65 66 for(int i=0; i < 7; i++) 67 { 68 cout << Record[i] << " "; 69 } 70 cout << endl; 71 72 return 0; 73 } 74 void FourColorLabel(int Adj[][7], int* Record, int cur, int n) 75 { 76 // Index exceed, then return 77 if(cur<0 || cur >= n) 78 { 79 // Break Out 80 return; 81 } 82 int clabel=0; 83 if(Record[cur] != 0) 84 { 85 int flag = 0; 86 for(clabel = Record[cur]+1; clabel <= 4; clabel++) 87 { 88 if(CheckOk(Adj, Record, cur, clabel)) 89 { 90 Record[cur] = clabel; 91 flag = 1; 92 break; 93 } 94 } 95 // IF 成功標記 96 if(flag == 1) 97 { 98 FourColorLabel(Adj, Record, cur+1, n); 99 } 100 // ELSE 不成功,返回上一步 101 else 102 { 103 FourColorLabel(Adj, Record, cur-1, n); 104 } 105 } 106 107 108 int flag = 0; 109 for(clabel = 1; clabel <= 4; clabel++) 110 { 111 if(CheckOk(Adj, Record, cur, clabel)) 112 { 113 Record[cur] = clabel; 114 flag = 1; 115 break; 116 } 117 } 118 // IF 成功標記 119 if(flag == 1) 120 { 121 FourColorLabel(Adj, Record, cur+1, n); 122 } 123 // ELSE 不成功,返回上一步 124 else 125 { 126 FourColorLabel(Adj, Record, cur-1, n); 127 } 128 129 } 130 131 bool CheckOk(int Adj[][7], int* Record, int cur, int clabel) 132 { 133 int i = 0; 134 for(;i < cur; i++) 135 { 136 // IF 相鄰 && 標記相同 137 if(Adj[i][cur]==1 && Record[i]==clabel) 138 { 139 return false; 140 } 141 } 142 return true; 143 }