zoukankan      html  css  js  c++  java
  • 无向图最大团/最大独立集

    定义:

    团:从一个无向图中找出一个点集,使得此集合中任意两个点都有一条直接相连的边。

    极大团:不是任何其他团的子图的团

    最大团:极大团中点数最多的团

    定理:

    一个图的最大独立集等于其补图的最大团


    Born_Kerbosch算法

    从后往前枚举每一个点为第一个点,选择的其他点为大于这个点的情况。用vec[]记录当前选择的点的集合,判断一个点是否可以选择,只需要枚举已选择的点是否都与之直接相连。

    优化: 用num[]记录每个点往后的最优情况(最大团大小),如果最优情况加上当前点数量都不能比已有答案ans更优,直接return。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=53;
    int mp[maxn][maxn];
    int ans_vec[maxn],vec[maxn],num[maxn];//num最后是递减的
        //答案团,当前团,每个位置开始遍历得到的最大团
    int n,ans;
    int dfs(int p,int siz){
        for(int i=p+1;i<=n;i++){//从前往后遍历每个点
            if(num[i]+siz<=ans)return 0;//最优性剪枝
            if(mp[p][i]){
                int can=1;
                for(int j=1;j<=siz;j++){
                    if(!mp[vec[j]][i]){
                        can=0;break;
                    }
                }
                if(can){//如果和已有集合均有边,则加入集合
                    vec[siz+1]=i;
                    if(dfs(i,siz+1))return 1;//更优的答案已经记录,无需更新
                }
            }
        }
        if(siz>ans){
            ans=siz;
            for(int i=1;i<=ans;i++)ans_vec[i]=vec[i];
            return 1;
        }
        return 0;
    }
    int deal(){
        ans=-1;
        for(int i=n;i>=1;i--){
            vec[1]=i;//开始只有i
            dfs(i,1);
            num[i]=ans;
        }
        return ans;
    }
    int main(){
        cin>>n;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                scanf("%d",&mp[i][j]);
            }
        }
        int res=deal();
        printf("%d
    ",res);
    }
    

    更快更短的写法:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 45;
    int n, k, g[N][N], ans[N], answer;
    void dfs(int sum, int v[], int len) {
    	answer = max(answer, sum);
    	int v1[N], len1;
    	for(int i = 1; i <= len; ++i) {
    		if(sum + len - i + 1 <= answer) break;
    		if(sum + ans[v[i]] <= answer) break;//最优性剪枝
    		len1 = 0;
    		for(int j = i + 1; j <= len; ++j) if(g[v[i]][v[j]]) v1[++len1] = v[j];
    		dfs(sum + 1, v1, len1);
    	}
    } 
    inline void MaxClique() {
    	int valid[N], len;
    	answer = ans[n] = 1;
    	for(int i = n - 1; i >= 1; --i) {
    		len = 0;
    		for(int j = i + 1; j <= n; ++j) if(g[i][j]) valid[++len] = j;
    		dfs(1, valid, len);
    		ans[i] = answer;
    	}
    }
    int main() {
    	scanf("%d%d", &n, &k);
    	for(int i = 1; i <= n; ++i)
    		for(int j = 1; j <= n; ++j)
    			scanf("%d", &g[i][j]);
    	MaxClique();
    	printf("%d
    ",answer);
    	return 0;
    }
    
  • 相关阅读:
    Stream 和 byte[] 之间的转换
    C# Process类_进程_应用程序域与上下文之间的关系
    C# Process类_进程管理器Demo
    C# attribute_特性
    SqlDataAdapter类
    SqlDataReader类
    SqlCommand类
    SqlConnection类
    DataTable类
    C# 语法技巧_三目运算_switch_case
  • 原文地址:https://www.cnblogs.com/ucprer/p/12006019.html
Copyright © 2011-2022 走看看