先上最大团定义:
最大团问题(Maximum Clique Problem, MCP)是图论中一个经典的组合优化问题,也是一类NP完全问题,在国际上已有广泛的研究,而国内对MCP问题的研究则还处于起步阶段,因此,研究最大团问题具有较高的理论价值和现实意义。
最大团问题又称为最大独立集问题(Maximum Independent Set Problem)。启发式算法。确定性算法有回溯法、分支限界法等,启发式算法、蚁群算法、顺序贪婪算法、DLS-MC算法和智能搜索算法等。
给定无向图G=(V,E)。如果UV,且对任意u,vU 有(u,v) E,则称U 是G 的完全子图。
团就是完全子图
最大团就是点数最多的完全子图
Given a graph G(V, E), a clique is a sub-graph g(v, e), so that for all vertex pairs v1, v2 in v, there exists an edge (v1, v2) in e. Maximum clique is the clique that has maximum number of vertex.
n<=50
深搜? dfs(p); visit[p]=1; 1. for(int i=1;i<=n;i++) if(visit[i]==1) dfs(i); 2.然后假设 i这个点就是在大团里面的啦: 如果存在 map[i][k]=0; visit[k]=0; 代表不能选取(回溯时记得标志清理干净) for(int i=1;i<=n;i++) if(visit[i]==1) dfs(i); 最后数visit的1的数目更新答案..... 显然这是暴力算法...不过先写一发,看看有多暴力
10s都T了...好怕怕。。
怎么优化?
最优化剪枝: ans-现在团中节点>可加入节点(加入该剪枝已经可以过)
好吧 以递增顺序枚举点才是搜索剪枝王道
1 2 3 4 5 这个状态就有 5!个不同的顺序描述方式
代码 #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <algorithm> #include <vector> #include <map> using namespace std; const int MAXN=60; int N; int Path[MAXN][MAXN]; int Set[MAXN];//用以寻找一个可能集合 int Ans; //判断新点是否与原集合里的点互相连通 bool is_clique( const int end, const int point ) { for( int i=1; i<end; ++i ) { if( !Path[ Set[i] ][point] ) { return false; } } return true; } //递归查找,查找算法简单明了,不再详说 void dfs( int depth, int now ) { if( depth+N-now+1 <= Ans ) { return; } for( int i=now; i<=N; ++i ) { if( is_clique(depth+1, i) ) { Set[depth+1]=i; dfs( depth+1, i+1 ); } } if( depth > Ans ) { Ans=depth; } } int main() { freopen("in","r",stdin); while( scanf("%d", &N)!=EOF && N ) { for( int i=1; i<=N; ++i ) { for( int j=1; j<=N; ++j ) { scanf("%d", &Path[i][j]); } } Ans=0; dfs( 0, 1 ); printf("%d ", Ans); } return 0; } //还可以设置一个DP[MAXN],用以记录i~N点集合里最大团数,可以利用这一点剪枝. DP[n]=DP[n+1]+1 or DP[n+1];
有点慢 尝试加入DP剪枝。
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <algorithm> #include <vector> #include <map> using namespace std; const int MAXN=60; int N; int Path[MAXN][MAXN]; int Set[MAXN];//用以寻找一个可能集合 int Ans; int DP[MAXN]; //判断新点是否与原集合里的点互相连通 bool is_clique( const int end, const int point ) { for( int i=1; i<end; ++i ) { if( !Path[ Set[i] ][point] ) { return false; } } return true; } //递归查找,查找算法简单明了,不再详说 void dfs( int depth, int now ) { if( depth+N-now+1 <= Ans||depth+DP[now] <=Ans ) { return; } for( int i=now; i<=N; ++i ) { if( is_clique(depth+1, i) ) { Set[depth+1]=i; dfs( depth+1, i+1 ); } } if( depth > Ans ) { Ans=depth; } } int main() { // freopen("a.in","r",stdin); while( scanf("%d", &N)!=EOF && N ) { for( int i=1; i<=N; ++i ) { for( int j=1; j<=N; ++j ) { scanf("%d", &Path[i][j]); } } memset(DP,0,sizeof(DP)); Ans=0; DP[N]=1; for(int i=N-1;i>=1;i--) { Set[1]=i; dfs( 1, i+1 ); DP[i]=Ans; } printf("%d ", DP[1]); } return 0; }并没有多大效果...但是还是少了1s 不错了