• 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]] | dp[i - 1][j - p[i][1]],这样的复杂度为O(n ^ 2),使用bitset可以优化,bitset即为一个二进制数集,即得到dp[i] = (dp[i - 1] << p[i][0]) | (dp[i - 1] << p[i][1])。

    代码:

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<string>
    #include<string.h>
    #include<math.h>
    #include<limits.h>
    #include<time.h>
    #include<stdlib.h>
    #include<map>
    #include<queue>
    #include<set>
    #include<stack>
    #include<vector>
    #include<bitset>
    #define LL long long
    using namespace std;
    vector <int> v[10005];
    int n, m;
    int cnt;
    int p[10005][2];
    bool vis[10005];
    bool color[10005];
    queue <int> q;
    bitset <10005> dp;
    void bfs(int u)
    {
        while(!q.empty())
            q.pop();
        int col[2] = {0};
        color[u] = 0;
        col[0] = 1;
        q.push(u);
        while(!q.empty())
        {
            int tmp = q.front();
            q.pop();
            int len = v[tmp].size();
            for(int i = 0; i < len; i++)
            {
                if(!vis[v[tmp][i]])
                {
                    vis[v[tmp][i]] = true;
                    color[v[tmp][i]] = !color[tmp];
                    col[color[v[tmp][i]]]++;
                    q.push(v[tmp][i]);
                }
            }
        }
        p[cnt][0] = col[0];
        p[cnt++][1] = col[1];
    }
    int main()
    {
        int T;
        while(~scanf("%d", &T))
        {
            while(T--)
            {
                for(int i = 0; i < 10005; i++)
                    v[i].clear();
                cnt = 0;
                memset(p, 0, sizeof p);
                memset(vis, 0, sizeof vis);
                memset(color, -1, sizeof color);
                scanf("%d%d", &n, &m);
                for(int i = 0; i < m; i++)
                {
                    int a, b;
                    scanf("%d%d", &a, &b);
                    v[a].push_back(b);
                    v[b].push_back(a);
                }
                for(int i = 1; i <= n; i++)
                {
                    if(!vis[i])
                    {
                        vis[i] = true;
                        bfs(i);
                    }
                }
                dp.reset();
                dp[0] = 1;
                for(int i = 0; i < cnt; i++)
                    dp = (dp << p[i][0]) | (dp << p[i][1]);
                int ans = 0;
                for(int i = 0; i <= n; i++)
                {
                    if(dp[i])
                        ans = max(ans, i * (n - i));
                }
                printf("%d
    ", ans - m);
            }
        }
        return 0;
    }
    

      

  • 相关阅读:
    Linux_MMU
    Linux_CPU寄存器简介
    Linux_数据段、代码段、堆栈段、BSS段的区别
    Linux_基本使用方法
    Linux_代码段和数据段的定义以及思考
    Linux_虚拟地址、线性地址和物理地址的转换
    Linux_微内核和单内核
    Linux_Linux的分段和分页机制
    教你实现一个朴实的Canvas时钟效果
    OpenMetric与时序数据库模型之主流TSDB分析
  • 原文地址:https://www.cnblogs.com/Apro/p/4679311.html
Copyright © 2020-2023  润新知