一個無向圖 G=(V,E),V 是點集,E 是邊集。取 V 的一個子集 U,若對於 U 中任意兩個點 u 和 v,有邊 (u,v)∈E,那麼稱 U 是 G 的一個徹底子圖。 U 是一個團當且僅當 U 不被包含在一個更大的徹底子圖中。php
G的最大團指的是定點數最多的一個團。ios
一、順序貪婪啓發式搜索算法算法
二、局部搜索啓發式算法網絡
三、智能搜索啓發式算法ide
四、遺傳算法ui
五、模擬退火算法spa
六、禁忌算法3d
七、神經網絡算法code
八、改進蟻羣算法-AntMCPhtm
看了所列出的算法,是否是有一種頭皮發麻的感受。反正我是這樣的感受...由於上面的東西我都不會...
若是你想看上面的東西,百度百科中有一些簡略的介紹,我太弱,沒看懂。
百度百科傳送門:最大團問題
下面說說經常使用的一種搜索算法
固然,這種算法很不高效,因此當圖中有 100 個點以上時,請慎用
先看看一個顯而易見的 DFS :
初始化:
從一個點 u 開始,把這個點加入到一個集合中,設爲 U。遍歷一遍全部和他相連的點,把他們放入另外一個集合 S1 中,接下來進行第一遍 DFS
第一遍 DFS :
從 S1 中選擇一個點 u1,這個點確定和集合 U 中的任何一個點相連。把集合 S1 中 u1 能訪問到的點加入到集合 S2 中,並把 u1 加入到集合 U 中,進行第二遍 DFS
第二遍 DFS :
從 S2 中選擇一個點 u2,這個點確定和集合 U 中的任何一個點相連。把集合 S2 中 u2 能訪問到的點加入到集合 S3 中,並把 u2 加入到集合 U 中,進行第三遍 DFS
第三遍 DFS :
從 S3 中選擇一個點 u3,這個點確定和集合 U 中的任何一個點相連。把集合 S3 中 u3 能訪問到的點加入到集合 S4 中,並把 u3 加入到集合 U 中,進行第四遍 DFS
......
最底層的 DFS :
當某個 S 集合爲空集的時候,DFS 結束,這時候咱們就找到了一個徹底子圖,用這個徹底子圖更新咱們的最大團。退出當前的 DFS,返回上層 DFS,接着找下一個徹底子圖,直到找完全部的徹底子圖
按照上面介紹的 DFS 方法,確定可以獲得一個最大團,由於該 DFS 把全部的徹底子圖都枚舉了一遍。可是這樣作的時間複雜度是否是過高了?
因而產生了下面的 DFS 過程,大體上和上面的 DFS 同樣,只不過有一些地方不太同樣了
首先,咱們先獲得後幾個點組成的最大團究竟是多大,(最開始的時候確定是最後一個點單獨構成一個最大團,點數爲1)而後咱們再 DFS:
初始化:
從一個點 u 開始,把這個點加入集合 U 中。將編號比它大的且和它相連的點加入集合 S1 中,爲了方便,將集合 S1 中的點有序,讓他們從小到大排列,進行第一遍 DFS
第一遍 DFS :
從 S1 中選擇一個點 u1,遍歷 S1 中,全部編號比 u1 大且和 u1 相連的點,其實也就是排在 u1 後面,而且和 u1 相連的點,將它們加入集合 S2 中。同理,讓 S2 中的點也按照編號也從小到大排列。將 u1 加入集合 U 中,進行第二遍 DFS
第二遍 DFS :
從 S2 中選擇一個點 u2,遍歷 S2 中,全部排在 u2 後面且和 u2 相連的點,並把它們加入集合 S3 中,讓 S3 中的點按照編號從小到大排列,將 u2 加入集合 U 中進行第三遍 DFS
第三遍 DFS :
從 S3 中選擇一個點 u3,遍歷 S3 中,全部排在 u3 後面且和 u3 相連的點,並把它們加入集合 S4 中,讓 S4 中的點按照編號從小到大排列,將 u3 加入集合 U 中進行第四遍 DFS
......
最底層的 DFS :
當某個 S 集合爲空時,DFS 過程結束,獲得一個只用後面幾個點構成的徹底子圖,並用它去更新只用後面幾個點構成的最大團。退出當前 DFS,返回上層 DFS,接着找下一個徹底子圖,直到找完全部的徹底子圖
上面的 DFS 過程,若是不加任何剪枝的話,其實和第一個 DFS 是差很少的,可是既然咱們都這樣 DFS 了,能不能想想怎麼剪枝呢?
假設咱們當前處於第 i 層 DFS,如今須要從 Si 中選擇一個 ui,把在 Si 集合中排在 ui 後面的和 ui 相連的點加入集合 S(i+1) 中,把 ui 加到集合 U 中
可能你們稍做思考以後就想到了一個剪枝:
剪枝1:若是 U 集合中的點的數量+1(選擇 ui 加入 U 集合中)+Si 中全部 ui 後面的點的數量 ≤ 當前最優值,不用再 DFS 了 |
還有什麼剪枝呢?
注意到咱們是從後往前選擇 u 的,也就是說,咱們在 DFS 初始化的時候,假設選擇的是編號爲 x 的點,那麼咱們確定已經知道了用 [x+1, n] ,[x+2, n],[x+3, n] ...[n,n] 這些區間中的點能構成的最大團的數量是多大
剪枝2:若是 U 集合中的點的數量+1(理由同上)+[ui, n]這個區間中能構成的最大團的頂點數量 ≤ 當前最優值,不用再 DFS了 |
有這兩個剪枝就夠了嗎?
不,咱們還能想出一個剪枝來:
剪枝3:若是 DFS 到最底層,咱們可以更新答案,不用再 DFS 了,結束整個 DFS 過程,也再也不返回上一層繼續 DFS 了 |
爲何?由於咱們若是再繼續日後 DFS 的話,點的編號變大了,可用的點變少了(可用的點在一開始 DFS 初始化的時候就肯定了,隨着不斷的加深 DFS 的層數,可用的點在不斷的減小)
有了上面三個剪枝,100 個點之內的圖,咱們也能很是快的出解了
可能有人會問,若是想知道最大團包含哪些節點該怎麼辦?
這還不簡單?每次 DFS 都會加一個點進入 U 集合中,DFS 到最底層,更新最大團數量的時候,U 集合中的點必定是一個徹底子圖中的點集,用 U 集合更新最大團的點集就好了
一、最大團點的數量=補圖中最大獨立集點的數量
二、二分圖中,最大獨立集點的數量+最小覆蓋點的數量=整個圖點的數量
三、二分圖中,最小覆蓋點的數量=最大匹配的數量
四、圖的染色問題中,最少須要的顏色的數量=最大團點的數量
一、先來一道裸題:ZOJ 1492 Maximum Clique
給了一個最多包含 50 個點的無向圖,讓求這個圖中最大團所包含的的點的數量
直接按照上面所講的 DFS 過程作就行
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 5 using namespace std; 6 7 struct MAX_CLIQUE { 8 static const int N=60; 9 10 bool G[N][N]; 11 int n, Max[N], Alt[N][N], ans; 12 13 bool DFS(int cur, int tot) { 14 if(cur==0) { 15 if(tot>ans) { 16 ans=tot; 17 return 1; 18 } 19 return 0; 20 } 21 for(int i=0; i<cur; i++) { 22 if(cur-i+tot<=ans) return 0; 23 int u=Alt[tot][i]; 24 if(Max[u]+tot<=ans) return 0; 25 int nxt=0; 26 for(int j=i+1; j<cur; j++) 27 if(G[u][Alt[tot][j]]) Alt[tot+1][nxt++]=Alt[tot][j]; 28 if(DFS(nxt, tot+1)) return 1; 29 } 30 return 0; 31 } 32 33 int MaxClique() { 34 ans=0, memset(Max, 0, sizeof Max); 35 for(int i=n-1; i>=0; i--) { 36 int cur=0; 37 for(int j=i+1; j<n; j++) if(G[i][j]) Alt[1][cur++]=j; 38 DFS(cur, 1); 39 Max[i]=ans; 40 } 41 return ans; 42 } 43 }; 44 45 MAX_CLIQUE fuck; 46 47 int main() { 48 while(scanf("%d", &fuck.n), fuck.n) { 49 for(int i=0; i<fuck.n; i++) 50 for(int j=0; j<fuck.n; j++) 51 scanf("%d", &fuck.G[i][j]); 52 printf("%d\n", fuck.MaxClique()); 53 } 54 return 0; 55 }
二、來一個稍微麻煩點的題:HDU 3585 maximum shortest distance
給了平面上 n 個點,要求選出 k 個點來,使得這 k 個點中,距離最近的兩個點的距離最大。n 最大爲50
二分答案後,若是兩個點之間的距離大於當前的判斷值,加邊,在用最大團跑一下,根據獲得最大團點的數量和 k 的大小關係,調整二分的上下界
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cmath> 5 6 using namespace std; 7 8 const int N=60; 9 10 struct MAX_CLIQUE { 11 bool G[N][N]; 12 int n, Max[N], Alt[N][N], ans; 13 14 bool DFS(int cur, int tot) { 15 if(cur==0) { 16 if(tot>ans) { 17 ans=tot; 18 return 1; 19 } 20 return 0; 21 } 22 for(int i=0; i<cur; i++) { 23 if(cur-i+tot<=ans) return 0; 24 int u=Alt[tot][i]; 25 if(Max[u]+tot<=ans) return 0; 26 int nxt=0; 27 for(int j=i+1; j<cur; j++) 28 if(G[u][Alt[tot][j]]) Alt[tot+1][nxt++]=Alt[tot][j]; 29 if(DFS(nxt, tot+1)) return 1; 30 } 31 return 0; 32 } 33 34 int MaxClique() { 35 ans=0, memset(Max, 0, sizeof Max); 36 for(int i=n-1; i>=0; i--) { 37 int cur=0; 38 for(int j=i+1; j<n; j++) if(G[i][j]) Alt[1][cur++]=j; 39 DFS(cur, 1); 40 Max[i]=ans; 41 } 42 return ans; 43 } 44 }; 45 46 struct Point { 47 double x, y; 48 49 double dis(Point A) { 50 return sqrt((x-A.x)*(x-A.x)+(y-A.y)*(y-A.y)); 51 } 52 } A[N]; 53 int n, m; 54 55 MAX_CLIQUE fuck; 56 57 void build(double R) { 58 fuck.n=n; 59 for(int i=0; i<n; i++) 60 for(int j=0; j<n; j++) { 61 if(A[i].dis(A[j])>=R) fuck.G[i][j]=1; 62 else fuck.G[i][j]=0; 63 } 64 } 65 66 int main() { 67 while(scanf("%d%d", &n, &m)!=EOF) { 68 for(int i=0; i<n; i++) { 69 scanf("%lf%lf", &A[i].x, &A[i].y); 70 } 71 double L=0, R=20000.0; 72 for(int T=0; T<40; T++) { 73 double dis=(L+R)/2.0; 74 build(dis); 75 if(fuck.MaxClique()>=m) L=dis; 76 else R=dis; 77 } 78 printf("%.2lf\n", L); 79 } 80 return 0; 81 }
三、來一個通常無向圖最大獨立集的題目:POJ 1419 Graph Coloring
給了一個有 n 個點 m 條邊的無向圖,要求用黑、白兩種色給圖中頂點塗色,相鄰的兩個頂點不能塗成黑色,求最多能有多少頂點塗成黑色。圖中最多有 100 個點
利用上面提到的結論:最大團點的數量=補圖中最大獨立集點的數量。創建補圖,求最大團便可
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 5 using namespace std; 6 7 struct MAX_CLIQUE { 8 static const int N=106; 9 10 bool G[N][N]; 11 int Max[N], Alt[N][N], x[N], y[N]; 12 int n, ans, *path, *res; 13 14 bool DFS(int cur, int tot) { 15 if(cur==0) { 16 if(tot>ans) { 17 swap(path, res), ans=tot; 18 return 1; 19 } 20 return 0; 21 } 22 for(int i=0; i<cur; i++) { 23 if(cur-i+tot<=ans) return 0; 24 int u=Alt[tot][i]; 25 if(Max[u]+tot<=ans) return 0; 26 int nxt=0; 27 for(int j=i+1; j<cur; j++) 28 if(G[u][Alt[tot][j]]) Alt[tot+1][nxt++]=Alt[tot][j]; 29 path[tot+1]=u; 30 if(DFS(nxt, tot+1)) return 1; 31 } 32 return 0; 33 } 34 35 int MaxClique() { 36 ans=0, memset(Max, 0, sizeof Max); 37 path=x, res=y; 38 for(int i=n-1, cur; i>=0; i--) { 39 path[1]=i, cur=0; 40 for(int j=i+1; j<n; j++) if(G[i][j]) Alt[1][cur++]=j; 41 DFS(cur, 1); 42 Max[i]=ans; 43 } 44 return ans; 45 } 46 }; 47 48 MAX_CLIQUE fuck; 49 50 int main() { 51 int T, m; 52 scanf("%d", &T); 53 for(int ca=1; ca<=T; ca++) { 54 scanf("%d%d", &fuck.n, &m); 55 memset(fuck.G, true, sizeof fuck.G); 56 for(int i=0, a, b; i<m; i++) { 57 scanf("%d%d", &a, &b); 58 fuck.G[a-1][b-1]=fuck.G[b-1][a-1]=0; 59 } 60 int ans=fuck.MaxClique(); 61 printf("%d\n", ans); 62 for(int i=1; i<=ans; i++) { 63 printf("%d", fuck.res[i]+1); 64 if(i==ans) printf("\n"); else printf(" "); 65 } 66 } 67 return 0; 68 }
四、來一個染色問題:POJ 1129 Channel Allocation
最多 26 廣播電臺...我仍是講抽象以後的題意吧:最多26個點的無向圖,要求相鄰的節點不能染成同一個顏色,問最少須要多少顏色染完全部的頂點
利用上面提到的結論:圖的染色問題中,最少須要的顏色的數量=最大團點的數量,建圖,跑最大團便可,另外,這題還須要構造解
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 5 using namespace std; 6 7 struct MAX_CLIQUE { 8 static const int N=27; 9 10 bool G[N][N]; 11 int n, Max[N], Alt[N][N], ans; 12 13 bool DFS(int cur, int tot) { 14 if(cur==0) { 15 if(tot>ans) { 16 ans=tot; 17 return 1; 18 } 19 return 0; 20 } 21 for(int i=0; i<cur; i++) { 22 if(cur-i+tot<=ans) return 0; 23 int u=Alt[tot][i]; 24 if(Max[u]+tot<=ans) return 0; 25 int nxt=0; 26 for(int j=i+1; j<cur; j++) 27 if(G[u][Alt[tot][j]]) Alt[tot+1][nxt++]=Alt[tot][j]; 28 if(DFS(nxt, tot+1)) return 1; 29 } 30 return 0; 31 } 32 33 int MaxClique() { 34 ans=0, memset(Max, 0, sizeof Max); 35 for(int i=n-1; i>=0; i--) { 36 int cur=0; 37 for(int j=i+1; j<n; j++) if(G[i][j]) Alt[1][cur++]=j; 38 DFS(cur, 1); 39 Max[i]=ans; 40 } 41 return ans; 42 } 43 }; 44 45 MAX_CLIQUE fuck; 46 char buff[30]; 47 48 int main() { 49 while(scanf("%d", &fuck.n), fuck.n) { 50 memset(fuck.G, 0, sizeof fuck.G); 51 for(int i=0; i<fuck.n; i++) { 52 scanf("%s", buff); 53 for(int j=2; buff[j]; j++) { 54 int u=buff[j]-'A'; 55 fuck.G[i][u]=fuck.G[u][i]=1; 56 } 57 } 58 int ans=fuck.MaxClique(); 59 if(ans==1) printf("1 channel needed.\n"); 60 else printf("%d channels needed. \n", ans); 61 } 62 return 0; 63 }
先來這麼4道題吧,暫時就碰見了這麼多,和二分圖有關的就懶得貼上來了