• HDU 5639 Deletion 二分+网络流


    题意:bc round 74 div1

    分析:

    考虑删掉的边的形态, 就是我们经常见到的环套树这种结构, 参考平时这种图给出的方法, 如果一个图的每个点的出边只有一条,

    那么一定会构成环套树这种结构. 于是问题可以转化成, 给无向图的每条边定向, 使得出度最大点的出度最小 (每个点的出度大小对应了删的次数).

    题解给出三种做法,对每条边定向,我采取第二种

    (官方题解)类似方法一, 要求的无非是每条边的归属问题, 对于每条边(a,b), 它可以属于a或者b,

    那么新建一个节点表示这条边并和a,b都相邻, 这样就得到了一个二分图. 左边是原图中的节点, 右边是原图中的边.

    二分每个左边每个节点的容量k, 如果右边的点能够完全匹配, 那么这个k就是可行的, 找到最小的k即可. 转化成二分图多重匹配问题.

    这个地方就是二分图,一部分是点,一部分是边,一条边和其左右两个点相连,这样二分每个节点的容量k,

    即源点和每个顶点建边,流量为k,然后按照题解建点和边的边,流量为1,然后每个表示边的点建一条边流向汇点,流量为1,

    ,当流过一条边时,就相当于给它定向,然后二分图求最大流,就可以了,判定,流量是不是等于m就行

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <queue>
    #include <cmath>
    using namespace std;
    typedef long long LL;
    const int mod=1e9+7;
    const int INF=0x3f3f3f3f;
    const int maxn=4e3+5;
    struct Edge
    {
        int from,to,cap,flow;
        Edge(int u,int v,int c,int d):from(u),to(v),cap(c),flow(d) {}
    };
    int x[maxn],y[maxn],n,m;
    struct dinic
    {
        int s,t;
        vector<Edge>edges;
        vector<int>G[maxn];
        int d[maxn];
        int cur[maxn];
        bool vis[maxn];
        void init()
        {
           edges.clear();
           for(int i=0;i<maxn;++i)
             G[i].clear();
        }
        bool bfs()
        {
            memset(vis,0,sizeof(vis));
            queue<int>q;
            q.push(s);
            d[s]=0;
            vis[s]=1;
            while(!q.empty())
            {
                int x=q.front();
                q.pop();
                for(int i=0; i<G[x].size(); i++)
                {
                    Edge &e= edges[G[x][i]];
                    if(!vis[e.to]&&e.cap>e.flow)
                    {
                        vis[e.to]=1;
                        d[e.to]=d[x]+1;
                        q.push(e.to);
                    }
                }
            }
            return vis[t];
        }
        int dfs(int x,int a)
        {
            if(x==t||a==0)return a;
            int flow=0,f;
            for(int &i=cur[x]; i<G[x].size(); i++)
            {
                Edge &e=edges[G[x][i]];
                if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow))))
                {
                    e.flow+=f;
                    edges[G[x][i]^1].flow-=f;
                    flow+=f;
                    a-=f;
                    if(a==0)break;
                }
            }
            return flow;
        }
        int maxflow(int s,int t)
        {
            this->s=s;
            this->t=t;
            int flow=0;
            while(bfs())
            {
                memset(cur,0,sizeof(cur));
                flow+=dfs(s,INF);
            }
            return flow;
        }
        void addedge(int u,int v,int c)
        {
            Edge x(u,v,c,0),y(v,u,0,0);
            edges.push_back(x);
            edges.push_back(y);
            int l=edges.size();
            G[u].push_back(l-2);
            G[v].push_back(l-1);
        }
    }solve;
    bool fun(int s)
    {
       solve.init();
       for(int i=1;i<=n;++i)
        solve.addedge(0,i,s);
       for(int i=1;i<=m;++i)
       {
        solve.addedge(x[i],i+n,1);
        solve.addedge(y[i],i+n,1);
       }
       for(int i=n+1;i<=n+m;++i)
       solve.addedge(i,n+m+1,1);
       int ans=solve.maxflow(0,n+m+1);
       if(ans==m)return 1;
       return 0;
    }
    int getans()
    {
        if(m==0)return 0;
        int l=0,r=m;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(fun(mid))r=mid;
            else l=mid+1;
        }
        return (l+r)>>1;
    }
    int main()
    {
        int T;
        scanf("%d", &T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            for(int i=1;i<=m;++i)
             scanf("%d%d",&x[i],&y[i]);
            printf("%d
    ",getans());
        }
        return 0;
    }
    View Code

    O(nm)O(nm)的做法, 只要在方法二的基础上继续改进就好了, 二分是没有必要的. 注意到每次增广的时候, 增光路中只有端点的容量会变化, 增广路中间的点的容量都不会变化. 那么之要每次增广到端点容量最小的那个点就好了.O(nm)O(nm)的做法, 只要在方法二的基础上继续改进就好了, 二分是没有必要的. 注意到每次增广的时候, 增光路中只有端点的容量会变化, 增广路中间的点的容量都不会变化. 那么之要每次增广到端点容量最小的那个点就好了.

  • 相关阅读:
    从百度文库下载的文档无法打开解决办法
    1
    sqlserver 或者 oracle 查询每科成绩前三名问题
    深度复制
    java 环境搭建
    我的小黑屋
    SQLSERVER 批量去掉外键约束
    linux mingling
    easyui datagrid 自定义editor并根据实际情况(自定义options)判断返回多样的html控件
    sqlserver 循环加类型转换拼接
  • 原文地址:https://www.cnblogs.com/shuguangzw/p/5247066.html
Copyright © 2020-2023  润新知