最大團問題
最大團
給定無向圖G=(V,E),其中V是非空集合,稱為頂點集;E是V中元素構成的無序二元組的集合,稱為邊集,無向圖中的邊均是定點的無序對,無序對常用圓括號“()”表示。
普及幾個概念:
- 無向圖:邊沒有方向。如果點A和B之間有一條邊,那麼既可以從A到B,也可以從B到A
- 完全圖:任意兩點之間都有邊相連。假設一個圖有$n$個點,那麼就應該有$\frac{n\times(n-1)}{2}$條邊
- 子圖:選取原圖中的部分結點,以及與該點相連的所有邊,構建出的新圖
瞭解上面幾個概念以後,給出最大團的定義: 在一個無向圖中找出一個點數最多的完全子圖
DFS求最大團
假設現在有這麼一個圖,我們用肉眼看一下就知道他的最大團數是3,(1,2,5),(1,4,5),(2,3,5)都是最大團
DFS求最大團的主要思路是構建一棵二叉子集樹,每次列舉新增或不添加當前結點到當前已經構建好的團中,如下圖,0就是不新增,1就是新增

這樣列舉的話時間複雜度是$O(2^n)$,考慮能不能剪枝,答案當然是可以的
首先思考一下,什麼情況下,列舉到的這個結點可以選?假設DFS執行下來我已經選了1和2,那麼3選還是不選?肯定不選,因為如果選了就無法與1和2構成完全圖
假設我在搜尋過程中已經得到了一個解bestn,如果剩下的所有點加上我當前已選的點,都不夠超過bestn,那我就沒必要往後枚舉了。這個說起來可能比較抽象,舉個例子,一共5個點,bestn已經更新成了3,當前樹的路徑是{1,0}(對應的選擇就是1選,2不選),假設現在列舉3不選,那麼還剩下兩個點4,5,不管4,5能不能與我當前的團構成完全圖,我就假設4,5都選上,那麼我最大團的結點數為3,根本沒有超過bestn,都無法得到更好的答案,我幹嘛要列舉呢,所以3不選的這棵子樹直接砍了
程式碼
import java.util.Scanner; public class Main { static int[][] map;//鄰接矩陣儲存圖 static int[] book;//記錄每個點選還是不選 static int bestn = Integer.MIN_VALUE; static int n,m;//n個點,m條邊 public static void main(String[] args) { Scanner cin = new Scanner(System.in); n = cin.nextInt(); m = cin.nextInt(); map = new int[n + 1][n + 1]; book = new int[n + 1]; for(int i = 0;i < m;i++) { int a = cin.nextInt(); int b = cin.nextInt(); map[a][b] = 1; map[b][a] = 1; } dfs(1,0); System.out.println(bestn); } static void dfs(int idx,int num) {//第idx個點,當前選了num個點 if(idx == n + 1) { bestn = Math.max(bestn,num); return; } for(int i = 0;i < 2;i++) {//二叉樹 if(i == 0) {//不選 if(num + n - idx > bestn) dfs(idx + 1,num); } else { //選 if(check(idx)) { book[idx] = 1; dfs(idx + 1,num + 1); book[idx] = 0; } } } } static boolean check(int idx) { for(int i = 1;i < idx;i++) if(book[i] == 1 && map[i][idx] != 1) return false; return true; } } /* 輸入: 5 7 1 2 2 3 5 3 4 5 1 4 1 5 2 5 輸出: 3 */
ofollow,noindex" target="_blank">HDU1530
題意:給定N個點的無向圖,讓你求最大團
Java tle了,不知道為什麼
import java.util.Scanner; public class Main { static int[][] map; static int[] book; static int bestn; static int n; public static void main(String[] args) { Scanner cin = new Scanner(System.in); while (true) { bestn = Integer.MIN_VALUE; n = cin.nextInt(); if(n == 0) break; map = new int[n + 1][n + 1]; book = new int[n + 1]; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { int flag = cin.nextInt(); map[i][j] = flag; } } dfs(1, 0); System.out.println(bestn); } } static void dfs(int idx, int num) { if (idx == n + 1) { bestn = Math.max(bestn, num); return; } for (int i = 0; i < 2; i++) { if (i == 0) { if (num + n - idx > bestn) dfs(idx + 1, num); } else { if (check(idx)) { book[idx] = 1; dfs(idx + 1, num + 1); book[idx] = 0; } } } } static boolean check(int idx) { for (int i = 1; i < idx; i++) if (book[i] == 1 && map[i][idx] != 1) return false; return true; } }