團
對於給定圖G=(V,E)。其中,V={1,…,n}是圖G的頂點集,E是圖G的邊集。圖G的團就是一個兩兩之間有邊的頂點集合。簡單地說,團是G的一個完全子圖。如果一個團不被其他任一團所包含,即它不是其他任一團的真子集,則稱該團為圖G的極大團(maximal clique)。頂點最多的極大團,稱之為圖G的最大團(maximum clique)。最大團問題的目標就是要找到給定圖的最大團。
一、最大團
給定無向圖G=(V,E),其中V是非空集合,稱為頂點集;E是V中元素構成的無序二元組的集合,稱為邊集,無向圖中的邊均是頂點的無序對,無序對常用圓括號“( )”表示。如果UV,且對任意兩個頂點u,v∈U有(u,v)∈E,則稱U是G的完全子圖。G的完全子圖U是G的團。G的最大團是指G的最大完全子圖。
如果UÍV且對任意u,v∈U有(u,v)不屬於E,則稱U是G的空子圖。G的空子圖U是G的獨立集當且僅當U不包含在G的更大的空子圖中。G的最大獨立集是G中所含頂點數最多的獨立集。
對於任一無向圖G=(V,E),其補圖G'=(V',E')定義為:V'=V,且(u,v)∈E'當且僅當(u,v)∉E。
如果U是G的完全子圖,則它也是G'的空子圖,反之亦然。因此,G的團與G'的獨立集之間存在一一對應的關系。特殊地,U是G的最大團當且僅當U是G'的最大獨立集。
通俗點講就是在一個無向圖中找出一個點數最多的完全圖。
二、回溯法求最大團問題
回溯法搜索解空間樹時,
根節點首先成為一個活結點,同時也成為當前的擴展節點。在當前擴展節點處,搜索向縱深方向移至一個新節點。這個新節點就成為一個新的活結點,並成為當前擴展節點。如果當前擴展節點不能再向縱深方向移動,則當前的擴展節點就成為死結點。此時,往回回溯至最近的一個活節點處,並使這個活結點成為當前的擴展節點。
回溯法以這種方式遞歸地在解空間中搜索,直至找到所有要求的解或解空間已無活結點為止。
搜索:
回溯法從根結點出發,按
深度優先策略遍歷解空間樹,搜索滿足約束條件的解。
剪枝:
在搜索至樹中任一結點時,先判斷該結點對應的部分解是否滿足約束條件,或者是否超出目標函數的界;也即判斷該結點是否包含問題的解,如果肯定不包含,則跳過對以該結點為根的子樹的搜索,即剪枝(Pruning);否則,進入以該結點為根的子樹,繼續按照深度優先的策略搜索。
一般來講,回溯法求解問題的基本步驟如下:
(1)針對所給問題,定義問題的解空間;確定易於搜索的解空間結構;以深度優先方式搜索解空間,並在搜索過程中利用Pruning函數剪去無效的搜索。
(2)無向圖
G的最大團問題可以看作是圖
G的頂點集
V的子集選取問題。因此可以用子集樹表示問題的解空間。設當前擴展節點
Z位於解空間樹的第
i層。在進入左子樹前,必須確認從頂點
i到已入選的頂點集中每一個頂點都有邊相連。在進入右子樹之前,必須確認還有足夠多的可選擇頂點使得算法有可能在右子樹中找到更大的團。
(3)用鄰接矩陣表示圖
G,
n為
G的頂點數,
cn存儲當前團的頂點數,
bestn存儲最大團的頂點數。
cn+n-i為進入右子樹的上界函數,當
cn+n-i<
bestn時,不能在右子樹中找到更大的團,利用剪枝函數可將
Z的右結點剪去。
(就不舉例了。想看例子,
百度百科。orz....第一次覺得百度講的東西還有點點清楚。
三、例題
1.hdoj1530 Maximum Clique
題意:給定N個點的無向圖,讓你求最大團。(最大團代碼模板)
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #define maxn 55 5 using namespace std; 6 int A[maxn][maxn],V[maxn]; 7 int cn,bestn; //當前節點數,最大節點數 8 int n; 9 int check(int x){ 10 for (int i=1;i<x;i++){ 11 if (V[i] && !A[x][i]) return 0; 12 } 13 return 1; 14 } 15 void dfs(int x){ 16 if (x>n){ //此處記錄最大團 17 bestn=max(cn,bestn); 18 return ; 19 } 20 if (check(x)){ 21 cn++; 22 V[x]=1; 23 dfs(x+1); 24 cn--; 25 } 26 if (cn+n-x>bestn){ 27 V[x]=0; 28 dfs(x+1); 29 } 30 } 31 int main(){ 32 ios::sync_with_stdio(false); 33 cin.tie(0);cout.tie(0); 34 while (cin >> n && n){ 35 for (int i=1;i<=n;i++){ 36 for (int j=1;j<=n;j++) cin >> A[i][j]; 37 } 38 cn=bestn=0; 39 memset(V,0,sizeof(V)); 40 dfs(1); 41 cout << bestn << endl; 42 } 43 return 0; 44 }