题意:输入一个二分图,通过加边使得这张图变成一个边数最多的完全二分图. 问最多能够新加多少条边. 注意重边是不允许的.
这题数据太弱了,导致好多只染一个联通块,把剩下的点都当成孤立点计算的代码都A过去了,然而题目题目可能出现多个联通块。
思路:首先二分图可以分成两类点X和Y, 完全二分图的边数就是X * Y.我们的目的是max {X * Y}, 并且X+ Y = n,所以只要X和Y的差最小,把原图黑白染色, 每个联通块有 ai 个黑点 bi 个白点, 于是就是要确定 X 应该选择 ai 还是 bi. 然后我们考虑dp, 因为每个联通块只能选择 ai 和 bi 中的一个, 所以想到分组背包,设ai, bi为物品 重量和价值都是n/2,背包容量也是n/2,这样就使得最优解接近n/2,这里和分组背包稍微有点不同,分组背包一个组合的物品可以不选,但是这里每个联通块里面我们必须选择一个,不过如果只是dp的话可能被某些数据卡死,比如 n = 10000, m = 0 , 解决这个问题我们只要加个剪枝,判断孤立点的个数足以使得X == Y or Y+1 or Y-1。
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n, m;
int sum1, sum2;
int dp[10005];
int vis[10005];
int sta[2][10005];
vector<int> mapn[10005];
void Init(int n)
{
memset(vis, -1, sizeof(vis));
for(int i = 0; i <= n; i++){
mapn[i].clear();
}
}
void Dfs(int rt) //染色
{
if(vis[rt] == -1)
vis[rt] = 0;
int len = mapn[rt].size();
for(int i = 0; i < len; i++){
int v = mapn[rt][i];
if(vis[v] == -1){
vis[v] = (vis[rt] ^ 1);
if(vis[v] == 0)
sum1++; // 统计黑点
else
sum2++; // 统计白点
Dfs(v);
}
}
}
int main()
{
int Test;
cin>>Test;
while(Test--){
cin>>n>>m;
Init(n);
int x, y;
for(int i = 0; i < m; i++){
cin>>x>>y;
mapn[x].push_back(y);
mapn[y].push_back(x);
}
int top = 0;
int num = 0, num1 = 0, num2 = 0;
for(int i = 1; i <= n; i++){
if(vis[i] == -1){
sum1 = 1;
sum2 = 0;
Dfs(i);
sta[0][top] = sum1; //保存黑点
sta[1][top++] = sum2; //保存白点
if(sum2 == 0){
num++; //孤立点
}
//粗略的填充完全二分图的两边,用于剪枝
else if(num1 < num2){
num1 += sum1 > sum2 ? sum1 : sum2;
num2 += sum2 > sum1 ? sum1 : sum2;
}
else{
num2 += sum1 > sum2 ? sum1 : sum2;
num1 += sum2 > sum1 ? sum1 : sum2;
}
}
}
//剪枝
if(abs(num1 - num2) <= num){
cout<<((n+1)/2)*(n/2) - m<<endl;
continue;
}
int minn = -1; // 保存选择i个联通块后最少的结点数。
memset(dp, 0, sizeof(dp));
for(int i = 0; i < top; i++){
int minnn = 10000000000; //用于更新minn
for(int j = n/2; j >= sta[0][i] || j >= sta[1][i]; j--){
int ans = 0;
//dp[j-sta[0][i]] >= minn 每个联通块都必须选
if(j >= sta[0][i] && dp[j-sta[0][i]] >= minn && ans < dp[j-sta[0][i]] + sta[0][i]){
ans = dp[j-sta[0][i]] + sta[0][i];
}
if(j >= sta[1][i] && dp[j-sta[1][i]] >= minn && ans < dp[j-sta[1][i]] + sta[1][i]){
ans = dp[j-sta[1][i]] + sta[1][i];
}
dp[j] = ans;
if(ans && ans < minnn) //更新
minnn = ans;
}
minn = minnn; //更新
}
cout<<(dp[n/2] * (n - dp[n/2])) - m<<endl;
}
return 0;
}
欢迎大牛拍砖。。。