著名的四色定理说到,“如果在平面上划出一些邻接的有限区域,那么可以用四种颜色来给这些区域染色,使得每两个邻接区域染的颜色都不一样”
另一个通俗的说法是,“任意一个无飞地的地图都可以用四种颜色染色,使得没有两个相邻国家染的颜色相同。”
定理的证明比较复杂,但可以确信:四种颜色是足够染完平面图,并且保证每两个邻接区域染的颜色都不一样。
在我的研究工作中,需要实现一个这样的算法,最初我用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 }