2 4 2 1 2 2 3 4 4 1 2 1 4 2 3 3 4
2 0
大致题意:
有n个点,m条边的二分图(可能不连通),问最多还能加多少条边变成完全二分图
思路:
显然每一连通块,都染成两种颜色,最后要尽量使两种颜色总数相同解才最优
显然有两种决策,不是染白就是染黑,01背包
dp[i][val]表示前i个连通块能染成同一色点数<=val的最大值
显然dp[scc][all/2]是最优解
#pragma comment(linker, "/STACK:1024000000,1024000000") #include <iostream> #include <cstring> #include <cmath> #include <queue> #include <stack> #include <list> #include <map> #include <set> #include <sstream> #include <string> #include <vector> #include <cstdio> #include <ctime> #include <bitset> #include <algorithm> #define SZ(x) ((int)(x).size()) #define ALL(v) (v).begin(), (v).end() #define foreach(i, v) for (__typeof((v).begin()) i = (v).begin(); i != (v).end(); ++ i) #define REP(i,n) for ( int i=1; i<=int(n); i++ ) using namespace std; typedef long long ll; #define X first #define Y second typedef pair<ll,ll> pii; const int N = 10000+100; const int M = 100000+1000; struct Edge{ int v,nxt; Edge(int v = 0,int nxt = 0):v(v),nxt(nxt){} }es[M*2]; int n,m; int ecnt; int head[N]; inline void add_edge(int u,int v){ es[ecnt] = Edge(v,head[u]); head[u] = ecnt++; es[ecnt] = Edge(u,head[v]); head[v] = ecnt++; } int col[N]; int cnt[N][2]; int top; int sum = 0; void dfs(int u,int fa){ col[u] = !col[fa]; cnt[top][col[u]]++; for(int i = head[u];~i;i = es[i].nxt){ int v = es[i].v; if(v == fa || col[v] != -1) continue; dfs(v,u); } } void ini(){ REP(i,n) head[i] = col[i] = -1,cnt[i][0] = cnt[i][1] = 0; col[0] = top = sum = ecnt = 0; } int dp[2][N]; int main(){ int T; cin>>T; while(T--){ scanf("%d%d",&n,&m); ini(); REP(i,m){ int u,v; scanf("%d%d",&u,&v); add_edge(u,v); } for(int i = n; i>= 1;i--){ if(col[i] != -1) continue; top++; dfs(i,0); if(cnt[top][0] == 0 || cnt[top][1] == 0) { cnt[top][0] = cnt[top][1] = 0; top--; } else { sum += cnt[top][0],sum += cnt[top][1]; } } int nd = n-sum; for(int i = 0;i <= sum/2;i++) dp[0][i] = 0; REP(i,top){ for(int j = 0; j <= sum/2; j++){ dp[i&1][j] = -1; if(j-cnt[i][0] >= 0 && dp[(i-1)&1][j-cnt[i][0]] != -1) dp[i&1][j] = dp[(i-1)&1][j-cnt[i][0]]+cnt[i][0]; if(j-cnt[i][1] >= 0 && dp[(i-1)&1][j-cnt[i][1]] != -1) { dp[i&1][j] = max(dp[(i-1)&1][j-cnt[i][1]]+cnt[i][1],dp[i&1][j]); } } int minn,maxx = sum-dp[top&1][sum/2]; int t = min(nd,maxx-dp[top&1][sum/2]); minn = dp[top&1][sum/2]+t; nd -= t; if(nd) minn += nd/2, maxx += nd/2 + (nd&1); printf("%d\n",minn*maxx-m); } }