[多校联考2019(Round 5 T2)]蓝精灵的请求(二分图染色+背包)
Posted birchtree
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[多校联考2019(Round 5 T2)]蓝精灵的请求(二分图染色+背包)相关的知识,希望对你有一定的参考价值。
[多校联考2019(Round 5)]蓝精灵的请求(二分图染色+背包)
题面
在山的那边海的那边住着 n 个蓝精灵,这 n 个蓝精灵之间有 m 对好友关系,现在蓝精灵们想要玩一个团队竞技游戏,需要分为两组进行,且每一组中任意两个蓝精灵都是好友。另外,他们还想要最小化每组蓝精灵内部的好友关系数之和。蓝精灵们怎么都想不到如何分组来进行游戏,所以找到你来帮助他们分组。(若第一组内部的好友关系数为 cnt1,第二组内部的好友关系数为 cnt2,则“每组蓝精灵内部的好友关系数之和”为 cnt1+cnt2)
\(n \leq 700\)
分析
实际上就是将一个图分成两个子图,使得每一个子图都是完全图。注意到没有边直接相连的点不能放在一个子图里。因此建原图的补图。那么补图中有边相连的点不能放在一个子图里。看到这个条件,我们想到了二分图染色。
于是我们对补图二分图染色。发现同一个联通块里相同颜色的点一定在同一个子图里。不同联通块里的两个点无论颜色都可以在一个子图里。
那么我们就可以预处理出\(f[i]\)表示是否存在一种分组方式使得某组的蓝精灵数为\(i\).具体方法是对于每个联通块,我们求出两种颜色的个数\(cnt[1],cnt[2]\),那么类似背包问题,可以用\(f[i]\)来更新\(f[i+cnt[1]],f[i+cnt[2]]\).注意要用滚动数组。
最后对于所有满足\(f[i]=1\)的\(i\),求边数最小值即可
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
#define maxn 700
using namespace std;
int n,m;
int g[maxn+5][maxn+5];
int invg[maxn+5][maxn+5];//反图
int col[maxn+5];
int cnt[5];
void dfs(int x,int c)
col[x]=c;
cnt[c]++;
for(int y=1;y<=n;y++)
if(y!=x&&g[x][y]==0)
if(!col[y]) dfs(y,3-c);
else if(col[y]==c)
printf("-1\n");
exit(0);
inline int get_e(int x)
return x*(x-1)/2;
int f[2][maxn+5];
//类似背包,记录能不能凑出一组点数为i的子图
int main()
int u,v;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d %d",&u,&v);
g[u][v]=1;
g[v][u]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j&&!g[i][j]) invg[i][j]=1;
int now=1;
f[1][0]=1;
for(int i=1;i<=n;i++)
if(!col[i])
cnt[1]=cnt[2]=0;
dfs(i,1);
for(int j=0;j<=n;j++)
f[now^1][j+cnt[1]]|=f[now][j];
f[now^1][j+cnt[2]]|=f[now][j];
now^=1;
//同一个二分图联通块里,不同颜色的点不能放一起
int ans=INF;
for(int i=1;i<n;i++)
if(f[now][i]) ans=min(ans,get_e(i)+get_e(n-i));
printf("%d\n",ans);
以上是关于[多校联考2019(Round 5 T2)]蓝精灵的请求(二分图染色+背包)的主要内容,如果未能解决你的问题,请参考以下文章
[多校联考2019(Round 5 T1)] [ATCoder3912]Xor Tree(状压dp)