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;
}