最大团最大独立集

点独立集

设无向图 G = < V , E > , V ∗ ⊂ V G=<V,E>, V^*\subset V G=<V,E>,VV ,若 V ∗ V^* V 中任意两个顶点均不相邻,则称 V ∗ V^* V G G G 的点独立集,或称独立集。最大点独立集的顶点个数记做 β 0 \beta_0 β0

G G G 中无孤立点, V ∗ V^* V G G G 中极大独立集,则 V ∗ V^* V 为极小独立集。

设无向图 G = < V , E > , V ∗ ⊂ V G=<V,E>, V^*\subset V G=<V,E>,VV ,若导出子图 G [ V ∗ ] G[V^*] G[V] 是完全图,则称 V ∗ V^* V 为团。最大团的顶点个数称为团数,记做 ν 0 \nu_0 ν0

  • V ∗ V^* V G G G 的团当且仅当 V ∗ V^* V G ˉ \bar{G} Gˉ 的独立集。

  • 一张图的最大独立集就是这张图的补图的最大团;

  • 一张图的补图通俗的讲就是把原图中的边断开,原图中没有的边连上;

  • 这样原图中任意两个顶点都不相连的子图,在补图中就是一个完全图。


模板

hdu1530 Maximum Clique

题意

给出一张图,求图中的最大团。

做法

似乎只能暴搜,,,。。。。

用一个数组 f [ i ] f[i] f[i] 记录 i . . . n i...n i...n 最大团的节点数,

倒序扫描 i i i ,把 i i i 作为第一个节点,把与 i i i 相连的点和 1 1 1 连起来,建一张新图,跑 d f s dfs dfs

扫描第 c n t cnt cnt 个点相连的点,对于第 i i i 个相连的点,把它与 i + 1 i+1 i+1 开始与第 c n t cnt cnt 个点相连的点连边

把它作为第 c n t + 1 cnt+1 cnt+1 个点继续 d f s dfs dfs

#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int N=55;
int n,a[N][N],f[N],ans;
/*
f[i]表示i~n最大团的节点数
a[][]邻接矩阵
ans答案
*/
vector<int> New[N]; //新图

bool dfs(int cnt)
{
	if (!New[cnt].size())
	{
		if (cnt>ans)
		{
			ans=cnt;
			return true;
		}
		return false;
	}
	for (int i=0; i<New[cnt].size(); ++i)
	{
		if (New[cnt].size()-i+cnt<=ans) return false;//最优性剪枝
		int x=New[cnt][i];
		if (f[x]+cnt<=ans) return false;//最优性剪枝
		New[cnt+1].clear();
		for (int j=i+1; j<New[cnt].size(); ++j)
			if (a[x][New[cnt][j]]) New[cnt+1].push_back(New[cnt][j]);
		if (dfs(cnt+1)) return true;
	}
	return false;
}

int MaxClique()
{
	memset(f,0,sizeof f);
	ans=0;
	for (int i=n; i; --i)
	{
		New[1].clear();
		for (int j=i+1; j<=n; ++j)
			if (a[i][j]) New[1].push_back(j);
		dfs(1);
		f[i]=ans;
	}
	return ans;
}

int main()
{
	while (scanf("%d",&n) && n)
	{
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=n; ++j) scanf("%d",&a[i][j]);
		printf("%d\n",MaxClique());
	}
	return 0;
}

一道题目

CF1105E Helping Hiasat

题意翻译

       你在某社交网站上面注册了一个新账号,这个账号有 n n n ( n ≤ 1 0 5 ) (n\leq 10^5) (n105)次记录。要么就是你更改过一次ID,要么就是一个ID为 s s s ( ∣ s ∣ ≤ 40 ) (|s|\leq 40) (s40)的朋友访问过你的空间。

       你有 m m m ( m ≤ 40 ) (m\leq 40) (m40)个朋友。每一个朋友都会访问你的空间至少一次。如果这一个朋友每一次访问你的空间的时候,你的ID和它的ID一样,那么他就会高兴。 求你最多能让多少人高兴。

题解

        一个朋友每次看到你的ID和他一样才会高兴,改一次ID只可以与一个朋友相同。把两次改ID之间来访的朋友连边,如果有一个朋友两两之间都没有连边,说明他们可以在这 n n n 次记录中被满足。于是问题转化成了求最大独立集的节点数,也就是求最大团。建图是可以用哈希把字符串映射成数字,用邻接矩阵存,求这张图补图中的最大团。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+5;
unsigned long long p[45],b[N],c[N],d[N];
int n,m,a[45][45],cmd[N],tot,cnt,f[45],ans;
char s[N][45];

void Hash(int num)
{
	int len=strlen(s[num]+1);
	unsigned long long val=0;
	for (int i=1; i<=len; ++i)
		val+=p[i-1]*s[num][i];
	d[num]=val;
	b[++tot]=val;
}

int query(unsigned long long x)
{
	return lower_bound(c+1,c+1+cnt,x)-c;
}

vector<int> v[45];
bool dfs(int cnt)
{
	if (!v[cnt].size())
	{
		if (cnt>ans)
		{
			ans=cnt;
			return true;
		}
		return false;
	}
	for (int i=0; i<v[cnt].size(); ++i)
	{
		if (v[cnt].size()-i+cnt<=ans) return false;
		int x=v[cnt][i];
		if (f[x]+cnt<=ans) return false;
		v[cnt+1].clear();
		for (int j=i+1; j<v[cnt].size(); ++j)
			if (a[x][v[cnt][j]]) v[cnt+1].push_back(v[cnt][j]);
		if (dfs(cnt+1)) return true;
	}
	return false;
}

int MaxClique(int n)
{
	for (int i=n; i; --i)
	{
		v[1].clear();
		for (int j=i+1; j<=n; ++j)
			if (a[i][j]) v[1].push_back(j);
		dfs(1);
		f[i]=ans;
	}
	return ans;
}

int main()
{
	scanf("%d%d",&n,&m);
	p[0]=1;
	for (int i=1; i<=40; ++i) p[i]=p[i-1]*131;
	for (int i=1; i<=n; ++i)
	{
		scanf("%d",&cmd[i]);
		if (cmd[i]==2) scanf("%s",s[i]+1),Hash(i);
	}
	sort(b+1,b+1+tot);
	for (int i=1; i<=tot; ++i)
		if (i==1 || b[i]!=b[i-1]) c[++cnt]=b[i];
	for (int i=1; i<=n;)
	{
		if (cmd[i]==1)
		{
			int j=i+1;
			for (; cmd[j]!=1 && j<=n; j++);
			j--;
			for (int k=i+1; k<j; ++k)
				for (int t=i+2; t<=j; ++t)
				{
					int x=query(d[k]),y=query(d[t]);
					a[x][y]=a[y][x]=1;
				}
			i=j+1;
		}
		else i++;
	}
	for (int i=1; i<=cnt; ++i)
		for (int j=1; j<=cnt; ++j)
			if (!a[i][j] && i!=j) a[i][j]=1;
			else a[i][j]=0;
	printf("%d\n",MaxClique(cnt));
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值