您的位置:首页 > 其它

二分图点染色 BestCoder 1st Anniversary($) 1004 Bipartite Graph

2015-07-26 10:02 330 查看
题目传送门

 /*
  二分图点染色:这题就是将点分成两个集合就可以了,点染色用dfs做, 剩下的点放到点少的集合里去
官方解答:首先二分图可以分成两类点X和Y, 完全二分图的边数就是|X|*|Y|.我们的目的是max{|X|*|Y|}, 并且|X|+|Y|=n.
修正:实现多连通块染色,然后贪心选择,将两个集合个数差大的连通块优先添加,能尽量使得un*vn最大
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
using namespace std;

const int MAXN = 1e4 + 10;
const int MAXM = 1e5 + 10;
const int INF = 0x3f3f3f3f;
vector<int> G[MAXN];
int col[2*MAXN];
bool vis[MAXN];
struct Block    {       //连通块
int u, v;
bool operator < (const Block &r) const  {
return u - v > r.u - r.v;
}
}b[MAXN];
int n, m, un, vn;

void DFS(int u, int c)    {
col[c]++; vis[u] = true;
for (int i=0; i<G[u].size (); ++i)  {
int v = G[u][i];
if (vis[v]) continue;
DFS (v, c ^ 1);
}
}

int main(void)  {       //BestCoder 1st Anniversary($) 1004 Bipartite Graph
//freopen ("D.in", "r", stdin);

int T;  scanf ("%d", &T);
while (T--) {
scanf ("%d%d", &n, &m);
for (int i=1; i<=n; ++i)    G[i].clear ();
for (int i=1; i<=m; ++i)    {
int u, v;   scanf ("%d%d", &u, &v);
G[u].push_back (v); G[v].push_back (u);
}

int color = 0;
memset (vis, false, sizeof (vis));
memset (col, 0, sizeof (col));
for (int i=1; i<=n; ++i)    {
if (vis[i]) continue;
DFS (i, color);    color += 2;
}
int cnt = 0;
for (int i=0; i<color; i+=2) {
b[++cnt].u = col[i];  b[cnt].v = col[i^1];
if (b[cnt].u < b[cnt].v)    swap (b[cnt].u, b[cnt].v);
}
sort (b+1, b+1+cnt);

un = vn = 0;
for (int i=1; i<=cnt; ++i)  {
if (un <= vn)   {
un += b[i].u;   vn += b[i].v;
}
else    {
un += b[i].v;   vn += b[i].u;
}
}
printf ("%d\n", un * vn - m);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: