HDU 1530 最大团问题 模板

HDU 1530 最大团问题

题意

首先需要明确最大团的定义,严格的定义请百度一下。

这里对于无向图,团的简单理解就是这个团里面所有的点相互之间都有直接的连边存在,最大的团就是这个团里面的点的数量最多。

这个题就是给我们一个用邻接矩阵来进行表示大图,然后让我们去找出最大团中点的个数。

题解思路

常用的做法在于搜索,但是如果想要提高效率,就需要找到合适的减枝策略。

先简述一下普通搜索的做法,然后我们再寻找合适的减枝思路。

初始化:从一个点 u 开始,把这个点加入集合 U 中。将编号比它大的且和它相连的点加入集合 S1 中,为了方便,将集合 S1 中的点有序,让他们从小到大排列,进行第一遍 DFS

第一遍 DFS :从 S1 中选择一个点 u1,遍历 S1 中,所有编号比 u1 大且和 u1 相连的点,其实也就是排在 u1 后面,并且和 u1 相连的点,将它们加入集合 S2 中。同理,让 S2 中的点也按照编号也从小到大排列。将 u1 加入集合 U 中,进行第二遍 DFS

第二遍 DFS :从 S2 中选择一个点 u2,遍历 S2 中,所有排在 u2 后面且和 u2 相连的点,并把它们加入集合 S3 中,让 S3 中的点按照编号从小到大排列,将 u2 加入集合 U 中进行第三遍 DFS

第三遍 DFS :从 S3 中选择一个点 u3,遍历 S3 中,所有排在 u3 后面且和 u3 相连的点,并把它们加入集合 S4 中,让 S4 中的点按照编号从小到大排列,将 u3 加入集合 U 中进行第四遍 DFS

​ …………

最底层的 DFS :当某个 S 集合为空时,DFS 过程结束,得到一个只用后面几个点构成的完全子图,并用它去更新只用后面几个点构成的最大团。退出当前 DFS,返回上层 DFS,接着找下一个完全子图,直到找完所有的完全子图

减枝方式1:如果第i层的DFS中,即使我们把Si中的所有的点都选了,但是还是之前计算的最优值,那么我们就可以减去这个分支了,显而易见。

减枝方式2:如果 U 集合中的点的数量(当我们选的点为ui时)+[ui, n]这个区间中能构成的最大团的顶点数量 当前最优值,不用再 DFS了。这里其实是需要我们记录以某个点开始,在所有大于这个点的编号中找到最大团中点的个数,使用dp数组来进行保存,有点动态规划的味道。

剪枝方式3:如果 DFS 到最底层,我们能够更新答案,不用再 DFS 了,结束整个 DFS 过程,也不再返回上一层继续 DFS 了。因为我们是按照编号来进行的,小编号包含大的编号的最大团情况,所以如果能够获得答案,就可以直接一路返回结束,不用再返回到上一层进行拓展了。

代码实现

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
typedef long long ll;
using namespace std;
const double eps=1e-6;
const int inf=0x3f3f3f3f;
const int MAXN=1e2+7;
int mp[MAXN][MAXN], dp[MAXN];
int n,best;
bool dfs(int s[], int sum, int cnt)// sum: 与u相连的顶点数量,cnt表示当前团的数量
{
    if(sum==0){// 当此团中最后一个点 没有 比起序号大 的顶点相连时
        if(cnt > best){// 问题1:best为最大团中顶点的数量
            best = cnt;
            return 1;
        }
        return 0;
    }
    int t[MAXN];
    for(int i=0; i<sum; i++){// 枚举每一个与 u 相连的顶点 s[i]
        if(cnt + (sum-i) <= best) return 0;// 剪枝1, 若当前 顶点数量cnt 加上还能够增加的最大数量 仍小于 best则 退出并返回false
        if(cnt + dp[s[i]] <= best) return 0;// 剪枝2, 若当前 顶点数量cnt 加上 包含s[i]的最大团顶点数 仍小于 best则 退出并返回false
        int k=0;
        for(int j=i+1; j<sum; j++){// 扫描 与u相连的顶点  中与 s[u]相连的顶点 并存储到 数组 t[]中,数量为k
            if(mp[s[i]][s[j]])
				t[k++]=s[j];
        }
        if(dfs(t, k, cnt+1)) return 1;
    }
    return 0;
}
int Max_clique()
{
    if(n<=0) return 0;
   	int adj[MAXN]; //邻接点 
    best=0;
    for(int i=n; i>=1;i--){ //需要倒叙进行 
        int k=0;
        for(int j=i+1; j<=n; j++){// 遍历 [i+1, n] 间顶点,
            if(mp[i][j]) 
				adj[k++]=j;
        }
        dfs(adj, k, 1);
        dp[i]=best; // 得出顶点i,出发构成最大团中顶点(这里的点编号需要大于等于i)数量
    }
    return best;
}
int main()
{
    while(scanf("%d",&n) != EOF){
    	if(n==0)
    		break;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
            scanf("%d",&mp[i][j]);
        printf("%d
",Max_clique());
    }
    return 0;
}

参考

解题思路参考:http://www.mamicode.com/info-detail-1678277.html

原文地址:https://www.cnblogs.com/alking1001/p/12966022.html