zoukankan      html  css  js  c++  java
  • BZOJ 3546 [ONTAK2010]Life of the Party (二分图最大匹配必须点)

    题解:给出一个二分图,问你取点哪个点会使得二分图匹配数减少。

    解法:其实就是问二分图匹配的必须点。先对初始二分图做一次最大匹配。

    现在考虑左边点,看残余网络上的边重新构图:如果是匹配边那么就从右往左连边,如果是非匹配边就从左往右连边。然后从每个非匹配点出发dfs标记每一个访问过的点。最后是匹配点且未被访问过的就是必须点。为什么呢?仔细考虑新构的图,从未匹配点出发的其实也是一条增广路,如果这条增广路能访问到 已匹配点 。那么我们就可以把这条增广路取反得到另一种匹配方案,也就是说这个被访问到的 已匹配点是非必须的。

    右边点原理相似。

    细节详见代码以及注释:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N=20000+10;
    const int M=200000+100;
    const int INF=0x3f3f3f3f;
    int n1,n2,m,s,t,id[M<<1];
    struct edge{
        int nxt,to,cap;
    }edges[M<<1];
    int cnt=1,x[M],y[M],head[N],cur[N];
    
    int add_edge(int x,int y,int z) {
        edges[++cnt].nxt=head[x]; edges[cnt].to=y; edges[cnt].cap=z; head[x]=cnt;
        return cnt;
    }
    
    int dep[N]; 
    queue<int> q;
    bool bfs() {
        while (!q.empty()) q.pop();
        memset(dep,0,sizeof(dep));
        dep[s]=1; q.push(s);
        while (!q.empty()) {
            int x=q.front(); q.pop();
            for (int i=head[x];i;i=edges[i].nxt) {
                edge e=edges[i];
                if (!dep[e.to] && e.cap) {
                    dep[e.to]=dep[x]+1;
                    q.push(e.to);
                }
            }
        }
        return dep[t];
    }
    
    int dfs(int x,int lim) {
        if (x==t) return lim;
        for (int& i=cur[x];i;i=edges[i].nxt) {
            edge e=edges[i];
            if (dep[x]+1==dep[e.to] && e.cap) {
                int flow=dfs(e.to,min(lim,e.cap));
                if (flow>0) {
                    edges[i].cap-=flow;
                    edges[i^1].cap=+flow;
                    return flow;  //找到一条增广路就要return 
                }
            }
        }
        return 0;
    }
    
    int Dinic() {
        int maxflow=0;
        while (bfs()) {
            for (int i=s;i<=t;i++) cur[i]=head[i];  //当前弧优化 
            while (int flow=dfs(s,INF)) maxflow+=flow;
        }
        return maxflow;
    }
    
    bool vis[N],match[N]; vector<int> G[N];
    bool dfs2(int x) {
        vis[x]=1;
        for (int i=0;i<G[x].size();i++) {
            int y=G[x][i];
            if (!vis[y]) dfs2(y);
        }
    }
    
    int main()
    {
        scanf("%d%d%d",&n1,&n2,&m);
        s=0; t=n1+n2+1;
        for (int i=1;i<=m;i++) {
            scanf("%d%d",&x[i],&y[i]);
            id[i]=add_edge(x[i],y[i]+n1,1);
            add_edge(y[i]+n1,x[i],0);
        }
        for (int i=1;i<=n1;i++) add_edge(s,i,1),add_edge(i,s,0);
        for (int i=1;i<=n2;i++) add_edge(n1+i,t,1),add_edge(t,n1+i,0);
        
        Dinic();
        
        //重新构图 
        for (int i=1;i<=n1+n2;i++) vis[i]=0,match[i]=0,G[i].clear();
        for (int i=1;i<=m;i++)
            if (edges[id[i]].cap) G[x[i]].push_back(n1+y[i]);  //不匹配边左到右连边 
            else G[n1+y[i]].push_back(x[i]),match[x[i]]=1;  //匹配边右到左连边 
        for (int i=1;i<=n1;i++) if (!match[i]) dfs2(i);  //从每个未匹配点出发dfs 
        for (int i=1;i<=n1;i++)
            if (match[i] && !vis[i]) printf("%d
    ",i);    //是匹配点且未被 未匹配点 访问的    
        
        for (int i=1;i<=n1+n2;i++) vis[i]=0,match[i]=0,G[i].clear();    
        for (int i=1;i<=m;i++)
            if (edges[id[i]].cap) G[n1+y[i]].push_back(x[i]);  //不匹配边右到左连边 
            else G[x[i]].push_back(n1+y[i]),match[n1+y[i]]=1;  //匹配边左到右连边
        for (int i=1;i<=n2;i++) if (!match[n1+i]) dfs2(n1+i);  //从每个未匹配点出发dfs  
        for (int i=1;i<=n2;i++)
            if (match[n1+i] && !vis[n1+i]) printf("%d
    ",i);  //是匹配点且未被 未匹配点 访问的
        return 0;
    } 
  • 相关阅读:
    623. Add One Row to Tree 将一行添加到树中
    771. Jewels and Stones 珠宝和石头
    216. Combination Sum III 组合总数三
    384. Shuffle an Array 随机播放一个数组
    382. Linked List Random Node 链接列表随机节点
    向github项目push代码后,Jenkins实现其自动构建
    centos下安装Jenkins
    python提取批量文件内的指定内容
    批处理实现:批量为文件添加注释
    python抓取每期双色球中奖号码,用于分析
  • 原文地址:https://www.cnblogs.com/clno1/p/10837407.html
Copyright © 2011-2022 走看看