演算法設計例題:最大團(回溯、分枝限界)
阿新 • • 發佈:2018-12-26
Description
Sample Input
給定無向圖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為最大團頂點數。
1
5 7
1 2
1 4
1 5
2 3
2 5
3 5
4 5
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;
}