AtCoder Regular Contest 099 (ARC099) E - Independence 二分图

原文链接https://www.cnblogs.com/zhouzhendong/p/9224878.html

题目传送门 - ARC099 E - Independence

题意

  给定一个有 $n$ 个节点, $m$ 条边的无向图,保证没有自环和重边。

  请你把所有的 $n$ 个节点分成两组,同组中的任意两个节点之间都有边直接连接。

  问连接同组节点的总边数最小为多少?如果不存在合法的划分方案,则输出 $-1$ 。

  数据范围: $n\leq 700, 0\leq m\leq \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;
}

  

posted @ 2018-06-25 16:27  zzd233  阅读(586)  评论(0编辑  收藏  举报