hdu 5313 Bipartite Graph(二分图染色+dp+bitset优化)

题目链接:

点击打开链接

题目大意:

给出一张二分图,问这张二分图还能最多加多少条边?

题目分析:

这道题的思路是这样的:

1.首先对于一张二分图,我们有两个点集,两点之间存在边的一定在不同点的集合中,那么我们我们可以利用染色将每个联通快中点分成两部分。

2.若两个点集的点数分别是n,m,那么边的总数是n*m。n+m==点的总数t,n*m=n*(t-n) = t*n - n^2,所以当n和m越接近时,边数愈多。

3.所以我们利用动态规划求取,相当于每组两个物品,必须选一个,如何选取才能使结果最接近n/2;

4.如果直接dp的话,那么我们需要n^2的复杂度。一定会超时,真是忧伤..........但是有一个数据结构能够帮我们解决这个问题

首先看dp[i][j]代表选过前i组物品后能否到达j个数量,因为只存0/1,所以我们可以用bitset来存。。

然后dp[i][j] = dp[i-1][j-p[i].first] | dp[i-1][j-p[i].second]。

所以我们可以直接位运算得到,也就是偏移p[i].first和p[i].second的长度,得到就是当前的状态,那么就是O(n*位移的复杂度),得到了优化。

最后必须怀有一颗感恩的心!!!STL大法好

代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <bitset>
#define MAX 10007

using namespace std;

int t,n,m,u,v;
int color[MAX];
int cnt[3];
//int dp[2*MAX];
bitset<MAX> dp;


typedef pair<int,int> PII;

vector<int> e[MAX];
vector<PII> p;

void add ( int u , int v )
{
    e[u].push_back ( v );
    e[v].push_back ( u );
}

void dfs ( int u , int c = 1)
{
    color[u]=c;
    cnt[color[u]]++;
    for ( int i = 0 ; i < e[u].size() ; i++ )
    {
        int v = e[u][i];
        if ( color[v] ) continue;
        dfs ( v , 3 - color[u] );
    }
}

void init ( )
{
    memset ( color , 0 , sizeof ( color ));
    for ( int i = 0 ; i < MAX ; i++ )
        e[i].clear();
    dp.reset();
    p.clear();
}


int main ( )
{
    scanf ( "%d" , &t );
    while ( t-- )
    {
        scanf ( "%d%d" , &n , &m );
        init ( );
        int ans = -m;
        while ( m-- )
        {
            scanf ( "%d%d" , &u , &v );
            add ( u , v );
        }
        for ( int i = 1 ; i <= n ; i++ )
            if (!color[i])
            {
                memset ( cnt , 0 , sizeof ( cnt ));
                dfs ( i );
                if ( cnt[1] || cnt[2] )
                    p.push_back ( make_pair ( cnt[1] , cnt[2]));
            }
        //dp[0] = 1;
        /*for ( int i = 0 ; i <  p.size() ; i++ )
            for ( int j = n/2 ; j >= 1 ; j-- )
                if ( ( j >=  p[i].first&&dp[j-p[i].first])
                    ||( j >=p[i].second&&dp[j-p[i ].second]) )
                    dp[j] = 1;*/
        //dp.reset();
        //cout << "YES" << endl;
        dp[0] = 1;
        for ( int i = 0 ; i < p.size() ; i++ )
            dp = dp<<p[i].first | dp<<p[i].second;

        int mid;
        for ( int i = 0 ;; i++ )
        {
            if ( dp[n/2-i] )
            {
                mid = n/2 - i;
                break;
            }
        }
        ans += mid*(n-mid);
        printf ( "%d\n" , ans );
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值