原文链接https://www.cnblogs.com/zhouzhendong/p/9224878.html
题目传送门 - ARC099 E - Independence
题意
给定一个有 $n$ 个节点, $m$ 条边的无向图,保证没有自环和重边。
请你把所有的 $n$ 个节点分成两组,同组中的任意两个节点之间都有边直接连接。
问连接同组节点的总边数最小为多少?如果不存在合法的划分方案,则输出 $-1$ 。
数据范围: $nleq 700, 0leq mleq cfrac{n(n-1)}2 $
题解
考虑到同组节点必须形成完全子图,这个性质和二分图恰好相反。
所以我们考虑在其补图上找一个二分图,并求出这个二分图两侧节点数有哪些可能。
对于每一个连通分量,我们进行黑白染色。如果不支持黑白染色,那么输出 $-1$ 。
(假设最终的二分图分为左右两侧)
记两种颜色的节点个数分别为 $x,y$ ,那么,既可以把 $x$ 个节点放入左侧,也可以把 $y$ 个节点放入左侧。
所有的连通分量的结果综合一下,变成一个背包问题,我用了 $DP$ ,然后赛后看大神们直接用 $bitset$,方便极了。
对于得到的所有可能的左侧节点数,我们算一下答案取 $min$ 即可。
代码
#include <bits/stdc++.h> using namespace std; const int N=2005; int n,m; int g[N][N]; int dp[N],e[N],vis[N],x,y,f=0; void dfs(int p,int t){ if (vis[p]){ if (vis[p]!=t+1) f=1; return; } vis[p]=t+1; if (t) x++; else y++; for (int i=1;i<=n;i++) if (i!=p&&!g[p][i]) dfs(i,t^1); } int calc(int x){ return x*(x-1)/2; } int main(){ scanf("%d%d",&n,&m); for (int i=1,a,b;i<=m;i++){ scanf("%d%d",&a,&b); g[a][b]=g[b][a]=1; } memset(dp,0,sizeof dp); dp[0]=1; for (int i=1;i<=n;i++){ if (vis[i]) continue; x=0,y=0; dfs(i,0); memset(e,0,sizeof e); for (int j=n;j>=0;j--) e[j+x]|=dp[j],e[j+y]|=dp[j]; for (int j=0;j<=n;j++) dp[j]=e[j]; } if (f){ puts("-1"); return 0; } int ans=n*n*2; for (int i=0;i<=n;i++) if (dp[i]) ans=min(ans,calc(i)+calc(n-i)); printf("%d",ans); return 0; }