标签:
题意:给一个二分图,问想让二分图变成完全二分图最多能加多少条边。
解法:图染色+dp+bitset优化。设最终的完全二分图两部分点集为A和B,A中点个数为x,B中点个数为y,边数则为x × y,答案即为x × y - m,那么用dp计算集合A中点个数的可能性。先用图染色计算每个连通分量里两种颜色点的个数,用dp[i][j]表示加入第i个连通分量时A集合中有j个点的可能性,可能为1,不可能为0,设联通分量为p,可以得到转移方程为dp[i][j] = dp[i - 1][j - p[i][0]] | dp[i - 1][j - p[i][1]],这样的复杂度为O(n ^ 2),使用bitset可以优化,bitset即为一个二进制数集,即得到dp[i] = (dp[i - 1] << p[i][0]) | (dp[i - 1] << p[i][1])。
代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string> #include<string.h> #include<math.h> #include<limits.h> #include<time.h> #include<stdlib.h> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #include<bitset> #define LL long long using namespace std; vector <int> v[10005]; int n, m; int cnt; int p[10005][2]; bool vis[10005]; bool color[10005]; queue <int> q; bitset <10005> dp; void bfs(int u) { while(!q.empty()) q.pop(); int col[2] = {0}; color[u] = 0; col[0] = 1; q.push(u); while(!q.empty()) { int tmp = q.front(); q.pop(); int len = v[tmp].size(); for(int i = 0; i < len; i++) { if(!vis[v[tmp][i]]) { vis[v[tmp][i]] = true; color[v[tmp][i]] = !color[tmp]; col[color[v[tmp][i]]]++; q.push(v[tmp][i]); } } } p[cnt][0] = col[0]; p[cnt++][1] = col[1]; } int main() { int T; while(~scanf("%d", &T)) { while(T--) { for(int i = 0; i < 10005; i++) v[i].clear(); cnt = 0; memset(p, 0, sizeof p); memset(vis, 0, sizeof vis); memset(color, -1, sizeof color); scanf("%d%d", &n, &m); for(int i = 0; i < m; i++) { int a, b; scanf("%d%d", &a, &b); v[a].push_back(b); v[b].push_back(a); } for(int i = 1; i <= n; i++) { if(!vis[i]) { vis[i] = true; bfs(i); } } dp.reset(); dp[0] = 1; for(int i = 0; i < cnt; i++) dp = (dp << p[i][0]) | (dp << p[i][1]); int ans = 0; for(int i = 0; i <= n; i++) { if(dp[i]) ans = max(ans, i * (n - i)); } printf("%d\n", ans - m); } } return 0; }
标签:
原文地址:http://www.cnblogs.com/Apro/p/4679311.html