zoukankan      html  css  js  c++  java
  • 网络流问题

    网络流

    网络流问题常见的求解目标有最大流(最小割)、最小费用最大流、上下界可行流等

    最小割

    最大流还有一个很重要的应用,就是求最小割,以下是一些定理,其实这些和二分图匹配里面的有点相似:

    最小割 = 最大流

    最大点权覆盖集 = 最小割

    最小点权独立集 = 总权值 - 最大点权覆盖集

    最小割的定义:把网络划分成两个集合,s,t使得在s集合和在t集合中的任意两点互相不相连,去掉的边就是割边,而这些割边代价总和就是割,最小割就是代价最小的划分方法

    一类最小割的题目还是挺明显,如果是要把存在点划分两个集合,求最小代价之类的,就很明显是最小割了

    最小割有一些挺经典的模型:

    1、平面图求最小割:

    这个做法就是,把平面每一部分面积当成点,然后相邻块连边,然后增设源点s和汇点t,分别在原来的入口出口的另一对角线上,和相应两部分边连边,这时候,每一个最小割,其实就是一个s到t的路径,那么求最短路就是最小割了

    2、最小权闭合

    这个做法是源点s连向正权值,负权值连向汇点t,之间关系连边容量INF,求出最小割之后,这个最小割就是最少损失,然后总权值 - 最小割得到的就是最大权闭合图的权值

    关于最小割输出路径

    根据题意,如果要s集合尽量多,就从t集合dfs,反之则从s集合dfs,不经过没有容量的边即可

    费用流

    一类K覆盖问题:

    这类问题一般表现为,一个区间或者一些点,每个可以最多被覆盖k次,然后有一些特殊的边可以走,但是只能走一次,这时候要求k覆盖后的最大权值

    其实就是费用流,建边把特殊边建起来,对应相应费用,然后其他边直接相连,费用为0,注意由于是要求最大代价,而算法是求最小费用,其实和KM匹配求最小一样的思路,把边权弄成负数,跑一下即可

    网络流的一些特殊问题

    上下界网络流:

    无源无汇有上下界最大流:

    这个要根据流量平衡来搞,建图先把边容量定成上限up - 下限down,然后每一个点,记录下流入流量和流出流量,然后设一个超级源s,超级汇t,s连接流量正的点,流量负的点连向t,然后跑最大流,跑完之后如果从s流出的流量都是满流,就是有解,每个边的真实流量就为当前边流量,加上原来的下限

    有源有汇有上下界最大流:

    建图方法一致,不过要多连上一条t->s容量为INF的边,这样跑一下最大流,t->s的流量就是答案

    有源有汇有上下界最小流:

    也是一样,不过t->s先不连,先求一次最大流,然后在连t->s,在做一次最大流把残余流量充分利用,然后t->s的流量就是答案

    分层网络流:

    这类题,以时间为单位,这样对于每个时间点就要建一层结点,时间上限不是很大的话,就可以每多一个时间点,就多一层结点,在原来的图上继续增广

    混合图找欧拉回路:

    欧拉回路入度等于出度,然后无向图先任意定向,然后记录每个点的度数和,度数和 / 2就是需要调整的边数,然后把源点连向正的,负的连向汇点,然后中间的边就是连无向边,因为只有无向边可以调整,然后跑一下最大流即可

    增加哪些边会使得最大流增加:

    这类问题其实就是对于满流的边,如果左边源点到他和他到汇点,能有一个残量网络,这条边就是可以增加的,利用两个dfs,分别从源点汇点出发即可

    最大密度子图:

    先要记录下每个结点的度数

    利用二分搜索来搜索答案g,然后根据这个建图判断,判断的方式为:

    源点与原图中每一个点连一条容量为m的边。原图中每一个点与汇点连一条容量为m+2*g-度数的边,再将原图中的无向边拆成两条有向边,容量都设为1.然后对此图求最大流,最后将(n*m-maxflow)/2 与0比较大小,如果它大于0,l = g,

    否则r = g

    POJ 3281 Dining

    题意:

    一个人有喜欢的饮料跟食物,现在给出这些关系,问最多能有多少人得到喜欢的饮料跟食物

    思路:

    网络流的题目往往难在建模,我们很容易想到的一种建模方法就是源点-食物-人-饮料-汇点

    但是这样的话可能一个人可以满足多次,所以为了加上人的这个限制条件,就需要将人拆点,并连上一条权值为1的边

    拆点也是对于点限制的很好的解决方法

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<queue>
     using namespace std;
     const int inf=1<<30;
    struct Graph{
        static const int M=100010,N=10010;
        int dis[N],cur[N],head[N],cnt;
        void Grap(){
            cnt=-1;
            memset(head,-1,sizeof head);
        }
        struct edge{
            int to,val,next;
        }e[M<<1];
        void _add(int u,int v,int val){
            cnt++;
            e[cnt].next=head[u];
            e[cnt].to=v;
            e[cnt].val=val;
            head[u]=cnt;
        }
        void add(int u,int v,int val){
            _add(u,v,val);
            _add(v,u,0);
        }
        bool bfs(int s,int t){
            queue<int> q;
            memset(dis,-1,sizeof dis);
            dis[s]=0;
            q.push(s);
            while(!q.empty()){
                int u=q.front();
                q.pop();
                for(int i=head[u];i!=-1;i=e[i].next){
                    int v=e[i].to;
                    if(dis[v]==-1&&e[i].val>0){
                        dis[v]=dis[u]+1;
                        q.push(v);
                    }
                }
            }
            return dis[t]!=-1;
        }
        int dfs(int s,int t,int maxflow){
            if(s==t)return maxflow;
            int res=0;
            for(int& i=cur[s];i!=-1;i=e[i].next){
                int v=e[i].to;
                if(dis[v]!=dis[s]+1||e[i].val<=0||res>=maxflow)continue;
                int f=dfs(v,t,min(e[i].val,maxflow-res));
                e[i].val-=f;
                e[i^1].val+=f;
                res+=f;
            }
            return res;
        }
        int Dinic(int s,int t){
            int ans=0;
            while(bfs(s,t)){
                memcpy(cur,head,sizeof head);
                ans+=dfs(s,t,inf);
            }
            return ans;
        }
    }mode;
     int main()
     {
         mode.Grap();
         int n,a,b;
         scanf("%d%d%d",&n,&a,&b);
         for(int i=1;i<=n;i++){
             int f,d,x;
             scanf("%d%d",&f,&d);
             for(int j=1;j<=f;j++){
                 scanf("%d",&x);
                 mode.add(x,a+i,1);
             }
            for(int j=1;j<=d;j++){
                scanf("%d",&x);
                mode.add(a+n+i,2*n+a+x,1);
            }    
        }
        for(int i=1;i<=n;i++) mode.add(a+i,a+n+i,1);
        for(int i=1;i<=a;i++) mode.add(0,i,1);
        for(int i=1;i<=b;i++) mode.add(2*n+a+i,2*n+a+b+1,1);
        cout<<mode.Dinic(0,2*n+a+b+1)<<endl;
        return 0;
     }
    View Code

    POJ 1087 A Plug for UNIX

    题意:

    有n个不同型号的插座(每种型号只有1个),m个不同的用电器且有着对应的插座型号(不同用电器对应的插座型号可能相同),同时有k种插座替代关系,例如A型号插座可以替代B型号插座(但B不一定能替代

    A)(插座型号和用电器均用字符串表示)。问最少有多少用电器没有插座用。

    思路:

    建立源点-插座-用电器-汇点的网络

    对于转换插座的话,可以直接在插座之间连上新的边,容量为inf(因为使用次数不限)

    最后跑一个最大流

    反思:在建图上出了很多的错误,建图连边的话不一定要很紧凑,这样建边不容易出错

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #include<map>
    #include<set>
    #include<string>
     using namespace std;
     const int inf=1<<30;
     int n,m,k;
     const int maxn=1e4+10;
     string t1,t2;
     map<string,int> m1;
     int cnt=0;
    struct Graph{
        static const int M=100010,N=10010;
        int dis[N],cur[N],head[N],cnt;
        void Grap(){
            cnt=-1;
            memset(head,-1,sizeof head);
        }
        struct edge{
            int to,val,next;
        }e[M<<1];
        void _add(int u,int v,int val){
            cnt++;
            e[cnt].next=head[u];
            e[cnt].to=v;
            e[cnt].val=val;
            head[u]=cnt;
        }
        void add(int u,int v,int val){
            _add(u,v,val);
            _add(v,u,0);
        }
        bool bfs(int s,int t){
            queue<int> q;
            memset(dis,-1,sizeof dis);
            dis[s]=0;
            q.push(s);
            while(!q.empty()){
                int u=q.front();
                q.pop();
                for(int i=head[u];i!=-1;i=e[i].next){
                    int v=e[i].to;
                    if(dis[v]==-1&&e[i].val>0){
                        dis[v]=dis[u]+1;
                        q.push(v);
                    }
                }
            }
            return dis[t]!=-1;
        }
        int dfs(int s,int t,int maxflow){
            if(s==t)return maxflow;
            int res=0;
            for(int& i=cur[s];i!=-1;i=e[i].next){
                int v=e[i].to;
                if(dis[v]!=dis[s]+1||e[i].val<=0||res>=maxflow)continue;
                int f=dfs(v,t,min(e[i].val,maxflow-res));
                e[i].val-=f;
                e[i^1].val+=f;
                res+=f;
            }
            return res;
        }
        int Dinic(int s,int t){
            int ans=0;
            while(bfs(s,t)){
                memcpy(cur,head,sizeof head);
                ans+=dfs(s,t,inf);
            }
            return ans;
        }
    }mode;
    int get(string s)
    {
        if(m1[s]==0) m1[s]=++cnt;
        return m1[s];
    }
    int main()
    {
        mode.Grap();
        int s=0,t=500;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>t1;
            mode.add(s,get(t1),1);    
        }
        cin>>m;
        for(int i=1;i<=m;i++){
            cin>>t1>>t2;
            mode.add(get(t2),200+i,1);
            mode.add(200+i,t,1);
        }
        cin>>k;
        for(int i=1;i<=k;i++){
            cin>>t1>>t2;
            mode.add(get(t2),get(t1),inf);
        }
        cout<<m-mode.Dinic(s,t)<<endl;
        return 0;
    }
    View Code

    POJ  2195 Going Home

    题意:

    有n个人,n座房子,每个人到不同的房子有不同的距离,现在给每个人分配一座房子求所有人到对应房子的最短距离

    思路:

    方法①很明显这是一个二分图,题目要求求的是一个最小权值完备匹配,所以可以直接将权值取负,利用KM算法求得最小值

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<iostream>
    using namespace std;
    const int N=205,INF=0x3f3f3f3f;
    struct node{
        int x,y;
    }g[N],f[N];
    int x,y,cnt1,cnt2;
    char mp[N][N];
    map<int,int> m1,m2;
    int n,m,t,u,v,w[N][N],la[N],a,lb[N],mat[N],d,upd[N];
    bool va[N],vb[N];
    bool dfs(int u) {
        va[u]=true; 
        for(int v=1;v<=n;v++) {
            if(vb[v]) continue;
            if (la[u]+lb[v]-w[u][v]==0) {
                vb[v]=true;
                if (!mat[v]||dfs(mat[v])) {
                    mat[v]=u;
                    return true;
                }
            } else upd[v]=min(upd[v],la[u]+lb[v]-w[u][v]);
        } 
        return false;
    }
    int KM() {
        memset(mat,0,sizeof(mat));
        for (int i=1;i<=n;i++) {
            la[i]=-INF;
            lb[i]=0;
            for(int j=1;j<=n;j++) {
                la[i]=max(la[i],w[i][j]); 
            }    
        }
        for(int i=1;i<=n;i++) {
            while(1){
                memset(va,false,sizeof(va));
                memset(vb,false,sizeof(vb));
                for(int j=1;j<=n;j++) upd[j]=INF;
                if(dfs(i)) break;
                d=INF;
                for(int j=1;j<=n;j++) if(!vb[j]) d=min(d,upd[j]);
                for (int j=1;j<=n;j++) {
                    if(va[j]) la[j]-=d;
                    if(vb[j]) lb[j]+=d;
                } 
            }
        }
        int ans=0;
        for (int i=1;i<=n;i++) ans+=w[mat[i]][i];
        return -ans; 
    }
    int main() {
        while(scanf("%d%d",&x,&y)&&x||y){
            memset(w,0,sizeof(w));
            cnt1=cnt2=0;
            for(int i=0;i<x;i++){
                scanf("%s",mp[i]);
                for(int j=0;j<y;j++){
                    if(mp[i][j]=='m') g[++cnt1].x=i,g[cnt1].y=j;
                    if(mp[i][j]=='H') f[++cnt2].x=i,f[cnt2].y=j;
                }
            }
            for(int i=1;i<=cnt1;i++)
                for(int j=1;j<=cnt1;j++)
                    w[i][j]=-(abs(g[i].x-f[j].x)+abs(g[i].y-f[j].y));
            n=cnt1;
            cout<<KM()<<endl;
        }
        return 0;
    } 
    View Code
  • 相关阅读:
    AJAX异步传输——以php文件传输为例
    js控制json生成菜单——自制菜单(一)
    vs2010中关于HTML控件与服务器控件分别和js函数混合使用的问题
    SQL数据库连接到服务器出错——无法连接到XXX
    PHP错误:Namespace declaration statement has to be the very first statement in the script
    【LeetCode】19. Remove Nth Node From End of List
    【LeetCode】14. Longest Common Prefix
    【LeetCode】38. Count and Say
    【LeetCode】242. Valid Anagram
    【LeetCode】387. First Unique Character in a String
  • 原文地址:https://www.cnblogs.com/overrate-wsj/p/12641566.html
Copyright © 2011-2022 走看看