1. 程式人生 > >演算法設計例題:最大團(回溯、分枝限界)

演算法設計例題:最大團(回溯、分枝限界)

Description

給定無向圖G=(V,E)。如果UV,且對任意u, v ∈ U 有 (u,v) ∈ E,則稱U是G的完全子圖。G的完全子圖U是G的團,當且僅當U不包含在G的更大的完全子圖中。G的最大團是指G中所含頂點數最多的團。

Input

輸入的第一行為測試樣例的個數T ,接下來有T個測試樣例。每個測試樣例的第一行是 頂點數n 和 邊數m ( n ≤ 20,m ≤ 400 ),接下來m行,每行兩個整數u和v,表示頂點u和v之間有一條邊相連。( 1 ≤ u < v ≤ n )。

Output

對應每個測試樣例輸出兩行,第一行格式為"Case #: M",其中'#'表示第幾個測試樣例(從1開始計),M為最大團頂點數。

Sample Input

1
5 7
1 2
1 4
1 5
2 3
2 5
3 5
4 5

Sample Output

Case 1: 3

題意分析:

完全子圖即是圖中任意的兩個頂點都有連線,與完全圖是一樣的。

例如:

                               

                 (a)                                        (b)                             (c)                            (d)

圖a是一個無向圖,圖b、c、d都是圖a的團,且都是最大團。

思路也十分的簡單,每次都放一個點進入圖中,因為是無向圖所以其生成的是子集樹,只需要把所有點都放進圖中嘗試一次便可。

剪枝部分使用簡單的剪枝,即沒試過的點+圖中的點<當前最優的點即可符合,否則直接跳過該點,嘗試下一個點。

總結:每次做dfs的題的時候先判斷是生成子集樹還是排序樹,再開始動手,一開始時沒判斷搞得整個程式碼越來越複雜,子集樹只要把每個點都搜一次就好,排序樹就要在遞迴裡for一次每一個點。(二叉樹和多叉樹的區別)。

程式碼如下:
#include <stdio.h>
#include <string.h>
int book[25][25],vist[25],que[25];//que陣列表示在圖中的點,book記錄邊,vist記錄訪問過的點
int n,m,maxx;
void dfs(int x,int sum){
    if(x>n){//搜完所有的點,遞迴出口
         maxx=sum;
         return ;
    }
    int vt=0;
    for(int j=0;j<sum;j++){//判斷與圖中的點是否兩兩相通
        if(book[x][que[j]] == 0){
                vt=1;
                break;
                }
            }
    if(vt == 0)//相通進入que陣列,並且圖中頂點+1
    {
        vist[x]=1;
        que[sum]=x;
        dfs(x+1,sum+1);
    }
      if(sum+n-x >maxx)//剪枝
         dfs(x+1,sum);
}
int main(){
    int t,vi=0;
    scanf("%d",&t);
    while(t--){
        scanf("%d %d",&n,&m);
        int x,y;
        memset(vist,0,sizeof(vist));
        memset(book,0,sizeof(book));
        maxx=0;
        for(int i=1;i<=m;i++){
            scanf("%d %d",&x,&y);
            book[x][y]=1;
            book[y][x]=1;
        }
             dfs(1,0);
        vi++;
       printf("Case %d: %d\n",vi,maxx);
    }
    return 0;
}