一、圖着色問題
(1)圖的m可着色判定問題
給定無向連通圖G和m種不同的顏色。用這些顏色為圖G的各頂點着色,每個頂點着一種顏色。是否有一種着色法使G中每條邊的2個頂點着不同顏色。
(2)圖的m可着色優化問題
若一個圖最少需要m種顏色才能使圖中每條邊連接的2個頂點着不同顏色,則稱這個數m為該圖的色數。
二、m可着色判定問題的解法
【算法】
(1)通過回溯的方法,不斷的為每一個節點着色,在前面cur-1個節點都合法的着色之后,開始對第cur-1個節點進行着色,
(2)這時候枚舉可用的m個顏色,通過和第cur-1個節點相鄰的節點的顏色,來判斷這個顏色是否合法
(3)如果找到那么一種顏色使得第cur-1個節點能夠着色,那么說明m種顏色的方案在當前是可行的。
(4)cur每次迭代加1,如果cur增加到N並通過了檢測,說明m種顏色是可滿足的。
(5)注意,這里只是要求判斷m種顏色是否可滿足,所以找到任何一種方案就可以了。
【代碼實現】
#include<iostream> #include<cstring> using namespace std; const int maxn = 105; int G[maxn][maxn]; int color[maxn]; bool ans; int n,m,k; void init(){ ans = 0; memset(G, 0 , sizeof G); memset(color, 0 , sizeof color); } void dfs(int cur){ if(cur > n) { ans = 1; return; } for(int i=1; i<=m; i++){ //對cur結點嘗試使用每一種顏色進行塗色 bool flag = 1; //cur之前的結點必被塗色 for(int j=1; j<cur; j++){ if(G[j][cur] == 1 && color[j] == i){ flag = 0;//只要有一個沖突都不行 break; } } //如果可以塗上i顏色,則考慮下一個結點的情況 if(flag){ color[cur] = i; dfs(cur + 1); } //如果到這一步第cur個結點無法着色,則返回探尋其他方案 else color[cur] = 0;//回溯 ; } } int main(){ while(cin>>n>>k>>m){ init(); for(int i=1; i<=k; i++){ int x,y; cin>>x>>y; G[x][y] = G[y][x] = 1; } dfs(1); cout<<ans<<endl; } return 0; }
三、m可着色拓展
【問題】在上述基礎上,求出m種顏色能夠給圖G塗色的總總方案數量
【算法】由於這個時候要求總方案數量,所以在找到一種可行方案后,總是進行回溯再搜索其他的解決方案,與上面不同,上面是只需要找出一種方案即可,所以如果找到了就不需要再回溯了,所以在這里只需要把回溯語句的位置寫到dfs語句的后面即可。
【代碼實現】
#include<iostream> #include<cstring> using namespace std; const int maxn = 105; int G[maxn][maxn]; int color[maxn]; int ans; int n,m,k; void init(){ ans = 0; memset(G, 0 , sizeof G); memset(color, 0 , sizeof color); } void dfs(int cur){ if(cur > n) { ans++; return; } for(int i=1; i<=m; i++){ //對cur結點嘗試使用每一種顏色進行塗色 bool flag = 1; //cur之前的結點必被塗色 for(int j=1; j<cur; j++){ if(G[j][cur] == 1 && color[j] == i){ flag = 0;//只要有一個沖突都不行 break; } } //如果可以塗上i顏色,則考慮下一個結點的情況 if(flag){ color[cur] = i; dfs(cur + 1); color[cur] = 0;//回溯 } } } int main(){ while(cin>>n>>k>>m){ init(); for(int i=1; i<=k; i++){ int x,y; cin>>x>>y; G[x][y] = G[y][x] = 1; } dfs(1); cout<<ans<<endl; } return 0; }
四、圖的m可着色優化問題
【問題】給定無向圖圖G,頂點數N,邊集E, 要求用最少的顏色使得圖中每一個頂點都塗上顏色,要求有邊直接相連的頂點不能塗同樣的顏色,問最少需要多少種顏色?
【算法】
(1)一種朴素的想法就是在上述圖着色的結論基礎上,使用二分法求出所需要的最小顏色數,范圍1~N。但是存在大量的重復計算,復雜度過高。
(2)可以在之前的DFS上加上一維顏色數,一邊增加頂點數一邊增加顏色數,留下能夠使得1~N頂點都滿足的最少的顏色數
【題目鏈接】分考場
題目大意是:有n個學生分考場,其中有k對學生認識,認識的學生不能在同一個考場,問最少需要多少個考場。
相當於有n個頂點,k條邊,有邊相連的頂點不能塗相同的顏色,問最少需要多少顏色。
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn = 105; const int inf = 0x3f3f3f3f; int n,k; int x,y; int room[maxn];//room[i]表示i號房間的當前人數 int stu[maxn][maxn];//stu[i][j]表示第i個房間的第 j個學生是誰 int G[maxn][maxn];//存圖 int ans ; void init(){ memset(room , 0, sizeof room); memset(stu, 0, sizeof stu); memset(G, 0, sizeof G); ans = inf; } //表示給第cur個學生安排房間 void dfs(int cur , int tot){ if(tot >= ans) return ; //如果學生都安排好了,則記錄這種方法所導出的ans if(cur > n){ ans = min(ans , tot); return ; } //如果還沒安排好,則給cur找合適的房間 for(int i=1; i<=tot; i++){ //看第i號房間里的所有人是否與其沖突 int len = room[i]; bool flag = 1; for(int j=1; j<=len; j++){ if(G[stu[i][j]][cur]){ flag = 0; break; } } //如果找到房間了 就去安排下一個學生 if(flag){ room[i]++;//這個房間里多住了cur stu[i][room[i]] = cur; //繼續深搜 dfs(cur+1, tot); //回溯 stu[i][room[i]] = 0; room[i]--; } } //如果這 tot個房間都不行,則需要新開間房給這個學生 room[tot+1]++; stu[tot+1][room[tot+1]] = cur; //繼續深搜 dfs(cur + 1 , tot + 1); //回溯 stu[tot+1][room[tot+1]] = 0; room[tot+1]--; } int main(){ while(cin>>n>>k){ init(); for(int i=1; i<=k; i++){ cin>>x>>y; G[x][y] = G[y][x] = 1; } dfs(1,0); cout<<ans<<endl; } }