点独立集
设无向图 G = < V , E > , V ∗ ⊂ V G=<V,E>, V^*\subset V G=<V,E>,V∗⊂V ,若 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>,V∗⊂V ,若导出子图 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ˉ 的独立集。
-
一张图的最大独立集就是这张图的补图的最大团;
-
一张图的补图通俗的讲就是把原图中的边断开,原图中没有的边连上;
-
这样原图中任意两个顶点都不相连的子图,在补图中就是一个完全图。
模板
题意
给出一张图,求图中的最大团。
做法
似乎只能暴搜,,,。。。。
用一个数组 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;
}
一道题目
题意翻译
你在某社交网站上面注册了一个新账号,这个账号有 n n n ( n ≤ 1 0 5 ) (n\leq 10^5) (n≤105)次记录。要么就是你更改过一次ID,要么就是一个ID为 s s s ( ∣ s ∣ ≤ 40 ) (|s|\leq 40) (∣s∣≤40)的朋友访问过你的空间。
你有 m m m ( m ≤ 40 ) (m\leq 40) (m≤40)个朋友。每一个朋友都会访问你的空间至少一次。如果这一个朋友每一次访问你的空间的时候,你的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;
}