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;
    } 
  • 相关阅读:
    Longest Palindromic Substring
    PayPal MLSE job description
    Continuous Median
    Remove Duplicates From Linked List
    Valid IP Address
    Longest substring without duplication
    Largest range
    Subarray sort
    Multi String Search
    Suffix Trie Construction
  • 原文地址:https://www.cnblogs.com/clno1/p/10837407.html
Copyright © 2011-2022 走看看