2022年多校冲刺NOIP联训测试3 T3 团(clique)
本题解来自于洛谷大佬@_HMZ_。
大概题意:给一张无向图,将这张图分为两个点集,每个点集构成的图是一个完全图(即团),使得两张完全图的边总数最小。
我们知道,一张完全图的边数是 $\frac{n\times(n-1)}{2}$。 假设第一个点集的点数是 $n$,第二个点集的点数是 $m$, 边数就是 $\frac{n\times(n-1)}{2} + \frac{m\times(m-1)}{2}$。
除 $2$ 可以忽略,所以变成 $n\times(n-1) + m\times(m-1)$。
根据分配律,$n^2 - n + m^2 -m$。 而我们又知道,$n+m$ 为定值。
所以我们只需要让 $n^2 + m^2$ 最小就好了。
这里 $n$ 和 $m$ 越接近,答案就越小,感性理解一下,不做证明。
这里还有一个 trick,我们先建出补图,对于一些点,如果它们在补图上互相不连通,那么这些点就是一张完全图。
这个其实很显然。
那么,我们就可以先建出补图,然后,对于一个连通块分别处理。
这个连通块中,如果 $i$ 和 $j$ 有边,那么 $i$ 和 $j$ 必定属于不同的点集(根据上面的结论)。 由于点集只有两个,所以这就变成了二分图染色,如果给出的图不是二分图,就可以输出无解。
那么如何使得 $n$ 和 $m$ 更接近?
我们已知二分图染色只有两种情况(即黑色变成白色,白色变成黑色)。
所以我们可以跑一遍 dp,来求出答案。
#include<iostream>
using namespace std;
int n,m,tot,head[1000005],col[1000005],fir,sec,val[1000005],c;
bool dp[2005],vis[2005][2005],f[2005];
int ans=1e9;
struct node
{
int y,nex;
}s[8000005];
void add(int i,int j)
{
s[++tot].y=j,s[tot].nex=head[i];
head[i]=tot;
}
void dfs(int now,int c)
{
col[now]=c;
if(c==1) ++fir;
else ++sec;
for(int i=head[now];i;i=s[i].nex)
{
int y=s[i].y;
if(col[y] && col[y]==c)
{
cout<<-1;
exit(0);
}
else if(!col[y])
dfs(y,3-c);
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
char u;
cin>>u;
vis[i][j]=u-'0',vis[i][j]=!vis[i][j];
if(vis[i][j] && i!=j) add(i,j);
}
dp[0]=true;
for(int i=1;i<=n;i++)
{
if(!col[i])
{
fir=0,sec=0;
dfs(i,1);
for(int j=0;j<=n;j++)
f[j]=dp[j],dp[j]=0;
for(int j=0;j<=n;j++)
{
if(j>=fir) dp[j]|=f[j-fir];
if(j>=sec) dp[j]|=f[j-sec];
}
}
}
for(int i=0;i<=n;i++)
{
if(dp[i])
{
int fir=i,sec=n-i;
ans=min(ans,fir*(fir-1)/2+sec*(sec-1)/2);
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号