题意:给定二分图,求添加的最多边数,使得添加之后还是二分图
思路:如果原图可以分成X,Y两个点集,那么边数最多为|X||Y|条。由于|X|+|Y|==n,所以需要使|X|与|Y|尽量接近。先对原图进行染色,对每个连通块,求出它的两种颜色的点数差,并且交换染的颜色,染色方案依然成立。不妨设染色0和1,cnt[i]表示颜色为i的点的个数,并假设cnt[1]总是大于等于cnt[0],|X|对应cnt[1],|Y|对应cnt[0],
(1)对于同一个连通块,由于可以改变第一次染的颜色,则有:
cnt[1]-cnt[0] = ±abs(cnt[1]-cnt[0])
(2)对不同连通块,有:
cnt[1]-cnt[0]=Σ±abs(cnt[1]-cnt[0])
左边表示最后的染色为1和0的点数差,也就是|X|-|Y|,右边是一个表达式,值取决于对每一个连通块取的正负情况。于是相当于在一系列正数前面添上正负号,使得最后结果是最小的正数,注意到每个数前面必须添上正号或符号,而所有正数的和是知道的,令为V,同时令第i个正数为Ai,于是转化为以V/2为背包容量、Ai为物品体积、求背包能放满的最大体积,用V减去2倍这个答案就是等号左边的最小值了。|X|-|Y|和|X|+|Y|都出来了,求出|X|、|Y|,|X||Y|-m便是答案。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
/* ******************************************************************************** */
#include <iostream> //
#include <cstdio> //
#include <cmath> //
#include <cstdlib> //
#include <cstring> //
#include <vector> //
#include <ctime> //
#include <deque> //
#include <queue> //
#include <algorithm> //
using
namespace
std;
//
//
#define pb push_back //
#define mp make_pair //
#define X first //
#define Y second //
#define all(a) (a).begin(), (a).end() //
#define foreach(i, a) for (typeof(a.begin()) it = a.begin(); it != a.end(); it ++) //
//
void
RI(vector<
int
>&a,
int
n){a.resize(n);
for
(
int
i=0;i<n;i++)
scanf
(
"%d"
,&a[i]);}
//
void
RI(){}
void
RI(
int
&X){
scanf
(
"%d"
,&X);}
template
<
typename
...R>
//
void
RI(
int
&f,R&...r){RI(f);RI(r...);}
void
RI(
int
*p,
int
*q){
int
d=p<q?1:-1;
//
while
(p!=q){
scanf
(
"%d"
,p);p+=d;}}
void
print(){cout<<endl;}
template
<
typename
T>
//
void
print(
const
T t){cout<<t<<endl;}
template
<
typename
F,
typename
...R>
//
void
print(
const
F f,
const
R...r){cout<<f<<
", "
;print(r...);}
template
<
typename
T>
//
void
print(T*p, T*q){
int
d=p<q?1:-1;
while
(p!=q){cout<<*p<<
", "
;p+=d;}cout<<endl;}
//
//
typedef
pair<
int
,
int
> pii;
//
typedef
long
long
ll;
//
typedef
unsigned
long
long
ull;
//
//
/* -------------------------------------------------------------------------------- */
//
template
<
typename
T>
bool
umax(T &a,
const
T &b) {
return
a >= b?
false
: (a = b,
true
);
}
const
int
maxn = 1e4 + 7;
struct
Graph {
vector<vector<
int
> > G;
void
clear() { G.clear(); }
void
resize(
int
n) { G.resize(n + 2); }
void
add(
int
u,
int
v) { G[u].push_back(v); }
vector<
int
> & operator [] (
int
u) {
return
G[u]; }
};
Graph G;
int
color[maxn], cnt[3];
void
dfs(
int
node,
int
c) {
color[node] = c;
cnt[c] ++;
for
(
int
i = 0; i < G[node].size(); i ++) {
int
v = G[node][i];
if
(!color[v]) dfs(v, 3 - c);
}
}
vector<
int
> dp;
int
a[maxn];
int
get(
int
n,
int
v) {
sort(a + 1, a + 1 + n);
dp.clear();
dp.pb(0);
int
now = 0, ans = 0;
bool
have[12345] = {
true
};
for
(
int
i = 1; i <= n; i ++) {
int
sz = dp.size();
for
(
int
j = 0; j < sz; j ++) {
int
buf = dp[j] + a[i];
if
(buf <= v && !have[buf]) {
if
(buf == v)
return
v;
dp.pb(buf);
have[buf] =
true
;
umax(ans, buf);
}
}
}
return
ans;
}
int
main() {
#ifndef ONLINE_JUDGE
freopen
(
"in.txt"
,
"r"
, stdin);
#endif // ONLINE_JUDGE
int
T, n, m;
cin >> T;
while
(T --) {
cin >> n >> m;
G.clear();
G.resize(n);
for
(
int
i = 0; i < m; i ++) {
int
u, v;
scanf
(
"%d%d"
, &u, &v);
G.add(u, v);
G.add(v, u);
}
memset
(color, 0,
sizeof
(color));
int
t = 0, total = 0;
for
(
int
i = 1; i <= n; i ++) {
if
(!color[i]) {
cnt[1] = cnt[2] = 0;
dfs(i, 1);
a[++ t] = cnt[1] - cnt[2];
if
(a[t] < 0) a[t] = -a[t];
total += a[t];
}
}
int
y = total / 2, r = total - 2 * get(t, y);
cout << (n + r) / 2 * (n - r) / 2 - m << endl;
}
return
0;
//
}
//
//
//
//
/* ******************************************************************************** */
|