HDU 5313 Bipartite Graph(二分图染色+01背包水过)

Problem Description

Soda has a bipartite graph with n vertices
and m undirected
edges. Now he wants to make the graph become a complete bipartite graph with most edges by adding some extra edges. Soda needs you to tell him the maximum number of edges he can add.

Note: There must be at most one edge between any pair of vertices both in the new graph and old graph.

Input

There are multiple test cases. The first line of input contains an integer T (1≤T≤100),
indicating the number of test cases. For each test case:

The first line contains two integers n and m, (2≤n≤10000,0≤m≤100000).

Each of the next m lines
contains two integer u,v (1≤u,v≤n,v≠u) which
means there‘s an undirected edge between vertex u and
vertex v.

There‘s at most one edge between any pair of vertices. Most test cases are small.

Output

For each test case, output the maximum number of edges Soda can add.

Sample Input

2
4 2
1 2
2 3
4 4
1 2
1 4
2 3
3 4

Sample Output

2
0

Source

BestCoder 1st Anniversary ($)

Recommend

hujie   |   We have carefully selected several similar problems for you:  5315 5314 5312 5311 5310

大致题意:

有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);
        }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-08 20:50:52

HDU 5313 Bipartite Graph(二分图染色+01背包水过)的相关文章

hdu 5313 Bipartite Graph(dfs染色 或者 并查集)

Problem Description Soda has a bipartite graph with n vertices and m undirected edges. Now he wants to make the graph become a complete bipartite graph with most edges by adding some extra edges. Soda needs you to tell him the maximum number of edges

HDU 5313——Bipartite Graph——————【二分图+dp+bitset优化】

Bipartite Graph Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 840    Accepted Submission(s): 285 Problem Description Soda has a bipartite graph with n vertices and m undirected edges. Now he w

hdu 5313 Bipartite Graph 完全二分图 深搜 bitset应用

Bipartite Graph Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 577    Accepted Submission(s): 154 Problem Description Soda has a bipartite graph with n vertices and m undirected edges. Now he

hdu 5313 Bipartite Graph(dfs+背包)

题意:n个点m条边,每条边的两个端点已知,求构成完全二分图能加的最多的边数: 参考:http://blog.csdn.net/acmhonor/article/details/47072399 思路:并不是一个二分图的题... n个点构成完全二分图是,两边的点数差值最小时边数最多(X+Y=n,求max(x*y)): 即在给定一些边的情况下,要求的是将点分在两个集合中数量差最小的情况: 先求出每个联通块中的点的情况,考虑孤立点的个数,通过贪心实现剪枝: 非贪心的情况使用背包,背包容量为n/2,推出

HDU 5313 Bipartite Graph (二分图着色,dp) TLE!!!

题意:Soda有一个$n$个点$m$条边的二分图, 他想要通过加边使得这张图变成一个边数最多的完全二分图. 于是他想要知道他最多能够新加多少条边. 注意重边是不允许的. 思路:二分图着色这个简单,主要是dp,还有时间限制.我觉得应该是找出所有连通分量,每个连通分量两边的点数存起来,后面统一进行DP.但是!!时间仅有1s,而且还100个例子,就是说大概要在100万的复杂度才行,可是有1万个点,当m=5000时,这就不行. 我不懂这道题如何下手才能保证一定正确且不超时,应该优化得很厉害~ 贴一个我认

HDU 5313 Bipartite Graph

题意:给一个二分图,问想让二分图变成完全二分图最多能加多少条边. 解法:图染色+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]] |

HDU 3033 I love sneakers! (01背包+反分组背包)

题意:给你 n,m,k,表示有k种鞋子共n双,你有m的容量: 每双鞋子有容量p和价值v:问是否买全k种鞋子,若能在容量为m的情况下最多能买到鞋子的价值为多少: 每双鞋子只能买一次(01背包),每种鞋子至少买一种(分组背包:每组只能有一个)与传统分组背包的限制相反. 注意初始化!!! #include<cstdio> #include<stdlib.h> #include<string.h> #include<string> #include<map&g

hdu 4044 GeoDefense (树形dp+01背包)

GeoDefense Time Limit: 12000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 663    Accepted Submission(s): 267 Problem Description Tower defense is a kind of real-time strategy computer games. The goal of towe

01背包水题篇之 HDU2955——Robberies

原来是想dp[i],表示不被抓概率为i所能抢到的最大钱(概率1-100) 后来看了别人的博客是dp[i]表示抢了i钱最大的不被抓概率,嗯~,弱菜水题都刷不动. 那么状态转移方程就是 dp[i]=max(dp[i],dp[i-money]*p),初始化dp(0~maxn)为0,dp[0]=1(1毛钱都没抢你抓个毛线啊,哥是良民~) 又是贴代码环节~ <span style="font-size:18px;">#include<iostream> #include&