zoukankan      html  css  js  c++  java
  • 网络流学习笔记

    本博客会随着后续刷题不断更新。

    网络流属于图论一部分,可以解决二分图匹配等问题。

    网络流图的特点为,有且仅有一个入度为0的点为起点,并且有且仅有一个出度为0的点为终点,且任意两点之间仅存在一条单向边,为该条路的流量限制,除起点及终点外,每个点流出等于流入,最后求起点到终点最大流量。

    网络流的建图方式不同于其他图,通常采用邻接表。由于建图为单向边,两点直接的边需要建两条。若A->B为edge1,B->A为edge2,若edge1为偶数,edge2为奇数,且edge2=edge1+1,则edge1^1=edge2,这样在对一条边操作时,可以O(1)查到其反向边。

    网络流通常采用Dinic法,每次bfs搜索增广路径,若查询到一条可行路径,则递归将流传到终点,正向边流量限制减去流量,同时反向边加上正向边减去的值,以此执行反悔操作。

    时间复杂度:在普通图下,时间复杂度$O(V^{2}E)$,在二分图中时间复杂度$O(sqrt{V}E)$。

    贴上网络流模板,采用邻接表建图。

    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair <int,int> pii;
    #define rep(i,x,y) for(int i=x;i<y;i++)
    #define rept(i,x,y) for(int i=x;i<=y;i++)
    #define per(i,x,y) for(int i=x;i>=y;i--)
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define de(x) cout<< #x<<" = "<<x<<endl
    #define dd(x) cout<< #x<<" = "<<x<<" "
    #define mes(a,b) memset(a,b,sizeof a)
    const ll inf=1e18;
    const int N=500010,M=300010;//N点数,M边数 
    int head[N],ver[M],edge[M],Next[M],d[N];
    int n,m,s,t,tot,maxflow;
    queue<int> q;
    void add(int x,int y,int z)//x到y建长度z单向边
    {
        ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;//邻接表建边 
        ver[++tot]=x;edge[tot]=0,Next[tot]=head[y],head[y]=tot;
    }
    bool bfs()//判断是否有增广路
    {
        mes(d,0);
        while(!q.empty()) q.pop();
        q.push(s);
        d[s]=1;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            for(int i=head[x];i;i=Next[i])
                if(edge[i]&& !d[ver[i]])
                {
                    q.push(ver[i]);
                    d[ver[i]]=d[x]+1;
                    if(ver[i]==t) return 1;
                }
        }
        return 0;
    }
    int dinic(int x,ll flow)
    {
        if(x==t) return flow;
        int rest=flow,k;
        for(int i=head[x];i&&rest;i=Next[i])
        {
            if(edge[i]&&d[ver[i]]==d[x]+1)
            {
                k=dinic(ver[i],min(rest,edge[i]));
                if(!k) d[ver[i]]=0;
                edge[i]-=k;
                edge[i^1]+=k;
                rest-=k;
            }
        }
        return flow-rest;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin>>n>>m;//n为点数,m为边数
        cin>>s>>t;//s起点,t终点 
        tot=1;
        rep(i,0,m)
        {
            int x,y,c;
            cin>>x>>y>>c;
            add(x,y,c);
        }
        int flow=0;
        while(bfs())
            while(flow=dinic(s,inf)) maxflow+=flow;
        cout<<maxflow<<"
    ";
        return 0;
    }

    经典例题:飞行员配对问题https://www.luogu.org/problem/P2756

    只需要将外国飞行员和源点相连,英国飞行员和终点相连,然后外国飞行员和英国飞行员建一条边权为1的边即可。

    贴上洛谷2756AC代码

    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair <int,int> pii;
    #define rep(i,x,y) for(int i=x;i<y;i++)
    #define rept(i,x,y) for(int i=x;i<=y;i++)
    #define per(i,x,y) for(int i=x;i>=y;i--)
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define de(x) cout<< #x<<" = "<<x<<endl
    #define dd(x) cout<< #x<<" = "<<x<<" "
    #define mes(a,b) memset(a,b,sizeof a)
    const ll inf=1e18;
    const int N=205,M=200000;
    int head[N],ver[M],edge[M],Next[M],d[N];
    int n,m,s,t,tot,maxflow;
    queue<int> q;
    
    void add(int x,int y,int z)
    {
        ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
        ver[++tot]=x;edge[tot]=0,Next[tot]=head[y],head[y]=tot;
    }
    
    bool bfs()
    {
        mes(d,0);
        while(!q.empty()) q.pop();
        q.push(s);
        d[s]=1;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            for(int i=head[x];i;i=Next[i])
                if( edge[i] && !d[ver[i]] )
                {
                    q.push(ver[i]);
                    d[ver[i]]=d[x]+1;
                    if(ver[i]==t) return 1;
                }
        }
        return 0;
    }
    
    int dinic(int x,ll flow)
    {
        if(x==t) return flow;
        int rest=flow,k;
        for(int i=head[x];i&&rest;i=Next[i])
        {
            if(edge[i]&&d[ver[i]]==d[x]+1)
            {
                k=dinic(ver[i],min(rest,edge[i]));
                if(!k) d[ver[i]]=0;
                edge[i]-=k;
                edge[i^1]+=k;
                rest-=k;
            }
        }
        return flow-rest;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        mes(head,0);
        cin>>n>>m;
        s=0,t=n+m+1;
        tot=1;
        int x,y;
        while(cin>>x>>y)
        {
            if(x==-1&&y==-1) break;
            add(x,y,1);
        }
        rept(i,1,n)
            add(s,i,1);
        rept(i,n+1,n+m)
            add(i,t,1);
        int flow=0;
        while(bfs())
            while(flow=dinic(s,inf)) maxflow+=flow;
        
        if(!maxflow)
        {
            cout<<"No Solution!
    ";
            return 0;
        }
        cout<<maxflow<<"
    ";
        for(int i=1;i<=n;i++)
        {
            for(int j=head[i];j;j=Next[j])
            {
                if( j%2==0&&!edge[j] )
                {
                    cout<<i<<" "<<ver[j]<<"
    ";
                }
            }
        }
        return 0;
    }

     2018ACM-ICPC亚洲区域赛南京站I题

    http://codeforces.com/gym/101981/attachments

    题意:有n个英雄,m个敌人,k瓶药剂,给出每个英雄可以消灭的敌人的编号。每个英雄只能消灭一个敌人,但每个英雄只能消灭一个敌人。现在有药剂,英雄喝了之后可以多消灭一个敌人,但每个英雄只能喝一瓶,问最多能消灭多少个敌人。

    下午在实验室队内自己开训练,和JC大佬那队一起开的,当时JC大佬他们队开的J题,没有看I题,当我们队AC之后JC大佬才看了I题,听到他们说,这题就差直接把网络流三个字写在题目里了。确实非常明显,很明显的一个匹配问题,如果每个英雄只能消灭一个敌人的话就是二分图匹配网络流裸题。刚开始建图错误,建成了这样。

    1

    直接把s和s2直接连了一条n+k的边,s2和n个英雄直接建了一条边权为2的边,然后WA了一发。后面发现,有部分英雄如果没有消灭敌人,可能会被当成药剂给别的英雄。

    然后找到了正确建图方式,就是这样

    将药剂和每个英雄的攻击一次分离。在这张图上直接跑最大流就可以了。

    最后贴上AC代码

    #include<bits/stdc++.h>
     
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    typedef pair <int,int> pii;
    #define rep(i,x,y) for(int i=x;i<y;i++)
    #define rept(i,x,y) for(int i=x;i<=y;i++)
    #define per(i,x,y) for(int i=x;i>=y;i--)
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define de(x) cout<< #x<<" = "<<x<<endl
    #define dd(x) cout<< #x<<" = "<<x<<" "
    #define mes(a,b) memset(a,b,sizeof a)
    const ll inf= 1e18;
    const int N=2005,M=1e6;
    int head[N],ver[M],edge[M],Next[M],d[N];
    int n,m,s,t,tot,maxflow;
    queue<int>q;
     
    void add(int x,int y,int z)
    {
        ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
        ver[++tot]=x,edge[tot]=0,Next[tot]=head[y],head[y]=tot;
    }
    bool bfs()
    {
        mes(d,0);
        while(!q.empty()) q.pop();
        q.push(s);
        d[s]=1;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            for(int i=head[x];i;i=Next[i])
            {
                if(edge[i]&&!d[ver[i]])
                {
                    q.push(ver[i]);
                    d[ver[i]]=d[x]+1;
                    if(ver[i]==t) return 1;
                }
            }
        }
        return 0;
    }
    int dinic(int x,ll flow)
    {
        if(x==t) return flow;
        int rest=flow,k;
        for(int i=head[x];i&&rest;i=Next[i])
        {
            if(edge[i]&&d[ver[i]]==d[x]+1)
            {
                k=dinic(ver[i],min(rest,edge[i]));
                if(!k) d[ver[i]]=0;
                edge[i]-=k;
                edge[i^1]+=k;
                rest-=k;
            }
        }
        return flow-rest;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        tot=1;
        int n,m,k;
        cin>>n>>m>>k;
        s=0;
        t=n+m+1;
        int s1=n+m+2,s2=n+m+3;
        rept(i,1,n)
        {
            int cnt;
            cin>>cnt;
            rept(j,1,cnt)
            {
                int y;
                cin>>y;
                add(i,n+y,1);
            }
        }
        add(s,s1,n);
        add(s,s2,k);
        rept(i,1,n) add(s1,i,1),add(s2,i,1);
        rept(i,1,m) add(n+i,t,1);
        int flow=0;
        while(bfs())
            while(flow=dinic(s,inf)) maxflow+=flow;
        cout<<maxflow<<"
    ";
        return 0;
    }
  • 相关阅读:
    「题解」300iq Contest 2 B Bitwise Xor
    「题解」agc031_e Snuke the Phantom Thief
    「题解」agc031_c Differ by 1 Bit
    「题解」NWRRC2017 Joker
    「题解」NWRRC2017 Grand Test
    「题解」USACO15FEB Fencing the Herd G
    zsh: command not found:xxx 解决方法
    curl
    redis搭建
    http 缓存分为客户端缓存/服务端缓存/数据库缓存
  • 原文地址:https://www.cnblogs.com/FZUzyz/p/11656547.html
Copyright © 2011-2022 走看看