Maximum Clique
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3490 Accepted Submission(s): 1873
The first line has one integer n, the number of vertex. (1 < n <= 50)
The following n lines has n 0 or 1 each, indicating whether an edge exists between i (line number) and j (column number).
A test with n = 0 signals the end of input. This test should not be processed.
5 0 1 1 0 1 1 0 1 1 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 0 0
4
这就是一道纯粹的最大团问题。首先,小编这里给出教科书版本的对一些图论基本名词的介绍:
1.无向完全图:设G=(V,E)为N阶无向图,若G中的任何顶点都以其余的N-1个顶点相邻,则称G为N阶无向图。
2.完全子图:给定G=(V,E),如果U为G的子图并且是完全图,则称为完全子图。
3.团:设U为G完全子图,当图U不包含G的更大完全子图时,称U是G的一个团。
4.最大团:G中所含顶点数最多的团。
有关求最大团的方法,一般的做法就是直接进行DFS搜索,而最朴素的DFS搜索便是:首先设最大团为一个空团,往其中加入一个顶点,然后依次考虑每个顶点,查看该顶点加入团之后仍然构成一个团,如果可以,考虑将该顶点加入团或者舍弃两种情况,如果不行,直接舍弃,然后递归判断下一顶点。
当然这样做肯定是对的,同样也能AC的了,但是你会发现运行时间会是几秒,相当的慢。不难想到,原因是因为我们搜索了大量肯定不会是极大团的点,所以在优化方面我们需要进行必要的剪枝。
很容易想到一个很简单的剪枝,就是当剩下的点全部加入当前团中都不大于当前最大团时就可以直接return。
再进行思考,因为这里我们的是无向图,所以假如1,2,3,4,5这五个点构成了极大团,那我们在搜索的时候可能就会按不同的顺序搜索了5! = 120次,小编这里把图当做有向图,点全部都从小编号点搜索到大编号的点,这样我们的方法就进一步的得到了优化。当然,小编这样的优化并不一定是最好的优化方法,小编也是跑了1000MS+才AC的,有些大神只需要几百MS就跑完了(牛逼啊!!!)。
这里小编给出我自己的最大团模板,很多最大团的题,都是可以直接套模板的。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=106;
int g[maxn][maxn],dp[maxn],stk[maxn][maxn],mx;
//int path[maxn],res[maxn];//记录最大团的点
int dfs(int n,int ns,int dep)//
{
if(ns == 0)
{
if(dep > mx)
{
//swap(path,res);
mx = dep;
return 1;
}
return 0;
}
int i,j,k,p,cnt;
for(i=0; i<ns; i++)
{
k = stk[dep][i];
cnt = 0;
if(dep + n - k <= mx) return 0;// 剪枝1, 若当前 顶点数量cnt 加上还能够增加的最大数量 仍小于 best则 退出并返回
if(dep + dp[k] <= mx) return 0;// 剪枝2, 若当前 顶点数量cnt 加上 包含adj[i]的最大团顶点数 仍小于 best则 退出并返回
for(j=i+1; j<ns; j++)
{
p = stk[dep][j];
if(g[k][p]) stk[dep+1][cnt++] = p;
}
//path[dep+1] = k;
if( dfs(n,cnt,dep+1) ) return 1;
}
return 0;
}
int clique(int n)
{
int i,j,ns;
for(mx=0, i=n-1; i>=0; i--)
{
//path[1] = i;
for(ns=0, j=i+1; j<n; j++)// 遍历 [i+1, n] 间顶点
if(g[i][j]) stk[1][ns++] = j;
dfs(n,ns,1);
dp[i] = mx;// 得出顶点 i, 出发构成最大团 中顶点数量
}
return mx;
}
int main()
{
int n;
while(scanf("%d",&n)&&n!=0)
{
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
scanf("%d",&g[i][j]);
int ans = clique(n);
printf("%d\n",ans);
/*for(int i=1; i<=ans; i++)
{
printf("%d",res[i]+1);
if(i == ans) printf("\n");
else printf(" ");
}*/
}
return 0;
}