这道题 是一个 最大团板子题。。。虽然才刚刚开始看最大团
首先什么是最大团,假设一个图里,有一些点和边构成一个子图,他们不与外界交流,完全就是个独立的个体,我们就可以叫他们是一个团,也就是 完全图,
最大团 通俗的讲,就是在一个图里找一个点数最多的 完全子图
那么因为只需要统计团中 节点的个数,那么 我们需要做的,就是 dfs 遍历一次所有连通的点,不断记录最大的团数目,直至搜索完整个图。
网上大牛们 有一些思路: (链接https://blog.csdn.net/weixin_37806707/article/details/60961435)
最暴力的方法:
将n个点加入一个集合U1,遍历U1中的点,每次选择1个点P1作为最大团的第一个点,即DFS的第一层。
遍历U1中剩下的点,选出与P1相连的点,加入集合U2,每次选择U2中的一个点P2作为最大团的第二个点,即DFS的第二层。
遍历U2中剩下的点,选出与P2相连的点,加入集合U3,每次选择U3中的一个点P3作为最大团的第三个点,即DFS的第三层(因为团即最大完全子图,要求各个点两两相连,因此只要从之前的集合中选择点即可,只有这些点是与之前的点相连的)。
……
以此类推,到DFS的最后一层m,即此时的Um为空集时,递归结束,求出了一个团。返回上一层继续递归其他情况……选出其中的最大团输出。
以上是最朴素的思路,但是这样的时间复杂度太高了,所以我们考虑剪枝:
1.显然,当DFS进行到某一层x时,如果当前层数(即团内点的数量)加上集合Ux中剩下点的数量小于等于已得出的答案(即使把剩下的点全部加入团中也无法更新答案),那我们可以直接返回上一层。
2.倒序遍历,采用DP进行优化,DP[i]表示第i个及之后的点所能组成的最大团。若当前层数为x,剩下的点从i开始,那么如果 x+DP[i]<=ans ,那么同样可以直接返回。
以下是 只进行稍微剪枝的暴力法代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN=205;
int n;
int ed[MAXN][MAXN];
int ses[MAXN];
int ans;
bool check(int endd,int x)
{
for( int i=1; i<endd; ++i )
{
if( !ed[ ses[i] ][x] )
{
return false;
}
}
return true;
}
void dfs(int dis, int now)
{
if( dis+n-now+1 <= ans ) //剪枝
{
return;
}
for( int i=now; i<=n; ++i )
{
if(check(dis+1, i))
{
ses[dis+1]=i;
dfs(dis+1, i+1);
}
}
ans=max(ans, dis);
}
int main()
{
while(~scanf("%d", &n) && n )
{
for( int i=1; i<=n; ++i )
{
for( int j=1; j<=n; ++j )
{
scanf("%d", &ed[i][j]);
}
}
ans=0;
dfs(0, 1);
printf("%d\n", ans);
}
return 0;
}
当然 这样确实 暴力一些 虽然比较容易理解,虽然加入初步的剪枝可以过,但是还是不够优秀。
之后我们引入以下优化方法:
倒叙记录之前所有点组成的团中点数目 可参照 dp 剪枝那里。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN=205;
int n;
int ed[MAXN][MAXN];
int ses[MAXN];
int ans;
int dp[maxn]; //加入dp数组 dp[i] 表示 i 以后能组成的最大团
bool check(int endd,int x)
{
for( int i=1; i<endd; ++i )
{
if( !ed[ ses[i] ][x] )
{
return false;
}
}
return true;
}
void dfs(int dis, int now) // dis 表示层数 now 表示当前点
{
if( dis+n-now+1 <= ans || dis+dp[now] <= ans) //剪枝
{
return;
}
for( int i=now; i<=n; ++i )
{
if(check(dis+1, i))
{
ses[dis+1]=i;
dfs(dis+1, i+1);
}
}
ans=max(ans, dis);
}
int main()
{
while(~scanf("%d", &n) && n )
{
for( int i=1; i<=n; ++i )
{
for( int j=1; j<=n; ++j )
{
scanf("%d", &ed[i][j]);
}
}
memset(dp,0,sizeof(dp));
dp[n] = 1;
ans = 0;
for(int i=n-1; i>=1; i--)
{
ses[1] = i;
dfs(1, i+1);
dp[i] = ans;//不断更新 dp
}
printf("%d\n", dp[1]);
}
return 0;
}