[hdu5313]二分图性质,dp

题意:给定二分图,求添加的最多边数,使得添加之后还是二分图

 思路:如果原图可以分成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;                                                                        //
}                                                                                    //
                                                                                     //
                                                                                     //
                                                                                     //
/* ******************************************************************************** */

转载于:https://www.cnblogs.com/jklongint/p/4681718.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值