zoukankan      html  css  js  c++  java
  • 【转】网络流总结

    推荐!!from好友的博客——【网络流】网络流小总结  http://www.cnblogs.com/KonjakJuruo/p/5560129.html

    原文转载如下——(当然,我也略修改了一些东西...)

    一、dinic最大流

    我的模板。模板上已经有了dfs上的优化(比我以前的快多了。。)优化啊优化。

     1 bool bfs(int st,int ed)
     2 {
     3     while(!q.empty()) q.pop();
     4     memset(d,-1,sizeof(d));
     5     q.push(st);
     6     d[st]=0;
     7     while(!q.empty())
     8     {
     9         int x=q.front();q.pop();
    10         for(int i=first[x];i!=-1;i=a[i].next)
    11         {
    12             int y=a[i].y;
    13             if(d[y]==-1 && a[i].d>0)
    14             {
    15                 d[y]=d[x]+1;
    16                 q.push(y);
    17             }
    18         }    
    19     }
    20     return (d[ed]!=-1);
    21 }
    22 
    23 int dfs(int x,int flow,int ed)
    24 {
    25     int k,p,r=0;
    26     if(x==ed) return flow;
    27     for(int i=first[x];i!=-1;i=a[i].next)
    28     {
    29         int y=a[i].y;
    30         if(d[y]==d[x]+1 && a[i].d>0)
    31         {
    32             p=minn(a[i].d,flow-r);
    33             p=dfs(y,p,ed);
    34             r+=p; //优化,把从这个点开始能流的全部流了
    35             a[i].d-=p;
    36             a[i^1].d+=p;
    37         }
    38                 if(r==flow) break; // 优化
    39     }
    40     if(!r) d[x]=-1;  //优化
    41     return r;
    42 }
    43 
    44 int dinic(int st,int ed)
    45 {
    46     int ans=0;
    47     while(bfs(st,ed))
    48     {
    49         int k;
    50         while(k=dfs(st,INF,ed)) ans+=k;
    51     }
    52     return ans;
    53 }
    54 
    55 dinic
    dinic

    二、上下界网络流

    建立超级源点ss,超级汇点tt(上图中的s改为ss,t改为tt)

    对于每条x到y,下界为k1,上界为k2的边(x,y,k1,k2),拆成如图这种形式:(x,y,k2-k1)(自由流),(ss,y,k1)(必须流入),(x,tt,k1)(必须流出)。

    (1)没有源点和汇点:

    可行流:跑一遍最大流,看是否满流。满流则有可行流。

    最大流:每条边加一个费用f=1,然后跑最大费用循环流(详见下面)。

    最小流:每条边加一个费用f=1,然后跑最小费用循环流。

    (2)有源点和汇点:

    原来的源点s,原来的汇点t。在s和t之间建边(t,s,INF),使原图变为无源汇的循环流。

    可行流:拆边后跑一遍最大流,满流则有可行流。

    最大流:去掉ss、tt(去掉后无论怎么跑都是已经满足了下界的可行流),然后从原来的源汇点s到t跑一遍最大流,让残余网络的增广路全部加上去。此时(s,t,INF)的那条边的反向边(t,s)的流量就是答案。

    最小路:去掉ss、tt后,从原来的源汇点t到s跑一遍最大流(逆向求解,让最多的流量还回去)。

    上下界网络路讲解:http://www.cnblogs.com/kane0526/archive/2013/04/05/3001108.html

    模板(poj2396)

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<queue>
      6 #include<vector>
      7 using namespace std;
      8 
      9 const int N=500,M=500,INF=(int)1e9;
     10 const int S=N+M+4;
     11 int n,m,s,t,ss,tt,len,sum,bk;
     12 int d[N+M+4],p[N][M],first[N+M+4],map[2][N][M];
     13 struct node{int x,y,d,next;}a[N*M*2];
     14 queue<int> q;
     15 
     16 int minn(int x,int y){return x<y ? x:y;}
     17 int maxx(int x,int y){return x>y ? x:y;}
     18 
     19 void ins(int x,int y,int d)
     20 {
     21     a[++len].x=x;a[len].y=y;a[len].d=d;
     22     a[len].next=first[x];first[x]=len;
     23     if(x==ss) sum+=d;
     24     swap(x,y);
     25     a[++len].x=x;a[len].y=y;a[len].d=0;
     26     a[len].next=first[x];first[x]=len;
     27 }
     28 
     29 void make_edge(int x,int y)
     30 {
     31     if(map[0][x][y]>map[1][x][y]) bk=0;
     32     if(map[0][x][y]==0) ins(x,y,map[1][x][y]);
     33     else
     34     {
     35         ins(ss,y,map[0][x][y]);
     36         ins(x,tt,map[0][x][y]);
     37         ins(x,y,map[1][x][y]-map[0][x][y]);
     38     }
     39 }
     40 
     41 void build(char c,int x,int y,int z)
     42 {
     43     int t1=0,t2=INF;
     44     if(c=='=') t1=t2=z;
     45     if(c=='>') t1=z+1;
     46     if(c=='<') t2=z-1;
     47     map[0][x][y]=maxx(map[0][x][y],t1);
     48     map[1][x][y]=minn(map[1][x][y],t2);
     49     
     50 }
     51 
     52 bool bfs(int st,int ed)
     53 {
     54     while(!q.empty()) q.pop();
     55     memset(d,-1,sizeof(d));
     56     q.push(st);
     57     d[st]=0;
     58     while(!q.empty())
     59     {
     60         int x=q.front();q.pop();
     61         for(int i=first[x];i!=-1;i=a[i].next)
     62         {
     63             int y=a[i].y;
     64             if(d[y]==-1 && a[i].d>0)
     65             {
     66                 d[y]=d[x]+1;
     67                 q.push(y);
     68             }
     69         }    
     70     }
     71     return (d[ed]!=-1);
     72 }
     73 
     74 int dfs(int x,int flow,int ed)
     75 {
     76     int k,p,r=0;
     77     if(x==ed) return flow;
     78     for(int i=first[x];i!=-1;i=a[i].next)
     79     {
     80         int y=a[i].y;
     81         if(d[y]==d[x]+1 && a[i].d>0)
     82         {
     83             p=minn(a[i].d,flow-r);
     84             p=dfs(y,p,ed);
     85             r+=p;
     86             a[i].d-=p;
     87             a[i^1].d+=p;
     88         }
     89     }
     90     if(!r) d[x]=-1;
     91     return r;
     92 }
     93 
     94 int dinic(int st,int ed)
     95 {
     96     int ans=0;
     97     while(bfs(st,ed))
     98     {
     99         int k;
    100         while(k=dfs(st,INF,ed)) ans+=k;
    101     }
    102     return ans;
    103 }
    104 
    105 int main()
    106 {
    107     freopen("a.in","r",stdin);
    108     freopen("a.out","w",stdout);
    109     int T;
    110     scanf("%d",&T);
    111     while(T--)
    112     {
    113         scanf("%d%d",&n,&m);
    114         int x,y,z,k;
    115         char c;
    116         s=n+m+1;t=s+1;ss=t+1;tt=ss+1;
    117         len=-1;sum=0;bk=1;
    118         memset(first,-1,sizeof(first));
    119         memset(map[0],0,sizeof(map[0]));
    120         memset(map[1],63,sizeof(map[1]));
    121         memset(p,0,sizeof(p));
    122         int sum1=0,sum2=0;
    123         for(int i=1;i<=n;i++)
    124         {
    125             scanf("%d",&x);
    126             map[0][s][i]=map[1][s][i]=x;
    127             make_edge(s,i);
    128             sum1+=x;
    129         }
    130         for(int i=n+1;i<=n+m;i++)
    131         {
    132             scanf("%d",&x);
    133             map[0][i][t]=map[1][i][t]=x;
    134             make_edge(i,t);
    135             sum2+=x;
    136         }
    137         if(sum1!=sum2) bk=0;
    138         scanf("%d",&k);
    139         for(int i=1;i<=k;i++)
    140         {
    141             scanf("%d%d",&x,&y);getchar();
    142             scanf("%c%d",&c,&z);
    143             if(x && y) build(c,x,y+n,z);
    144             if(!x && y)
    145                 for(int j=1;j<=n;j++)
    146                     build(c,j,y+n,z);
    147             if(x && !y)
    148                 for(int j=1;j<=m;j++)
    149                     build(c,x,j+n,z);
    150             if(!x && !y)
    151                 for(int j=1;j<=n;j++)
    152                     for(int l=1;l<=m;l++)
    153                         build(c,j,l+n,z);
    154         }
    155         for(int i=1;i<=n;i++)
    156             for(int j=n+1;j<=n+m;j++)
    157                 make_edge(i,j);
    158         ins(t,s,INF);
    159         if(!bk || dinic(ss,tt)!=sum) printf("IMPOSSIBLE
    ");
    160         else 
    161         {
    162             for(int i=0;i<len;i++)
    163             {
    164                 x=a[i].x;y=a[i].y;
    165                 if(x<=n+m && y<=n+m)
    166                 {
    167                     if(!p[x][y-n]) p[x][y-n]=map[0][x][y]+a[i^1].d;
    168                 }
    169             }
    170             for(int i=1;i<=n;i++)
    171             {
    172                 for(int j=1;j<=m;j++)                    
    173                     printf("%d ",p[i][j]);
    174                 printf("
    ");
    175             }
    176         }
    177         printf("
    ");
    178     }
    179     return 0;
    180 }
    181 
    182 poj2396
    poj2396

    三、最小费用最大流

    在满足最大流的前提下求最小费用,就是在bfs的时候找一条费用最小的增广路。

     1 void ins(int x,int y,int d,int f)
     2 {
     3     a[++len].x=x;a[len].y=y;a[len].d=d;a[len].f=f;
     4     a[len].next=first[x];first[x]=len;
     5     a[++len].x=y;a[len].y=x;a[len].d=0;a[len].f=-f;
     6     a[len].next=first[y];first[y]=len;
     7 }
     8 
     9 int bfs(int st,int ed)
    10 {
    11     while(!q.empty()) q.pop();
    12     memset(pre,-1,sizeof(pre));
    13     memset(dis,63,sizeof(dis));
    14     memset(in,0,sizeof(in));
    15     memset(flow,0,sizeof(flow));
    16     pre[st]=0;dis[st]=0;in[st]=1;flow[st]=INF;q.push(st);
    17     while(!q.empty())
    18     {
    19         int x=q.front();in[x]=0;q.pop();
    20         for(int i=first[x];i!=-1;i=a[i].next)
    21         {
    22             int y=a[i].y;
    23             if(a[i].d && dis[y]>dis[x]+a[i].f)
    24             {
    25                 dis[y]=dis[x]+a[i].f;
    26                 pre[y]=i;
    27                 flow[y]=minn(a[i].d,flow[x]);
    28                 if(!in[y]) {in[y]=1;q.push(y);}
    29             }
    30         }
    31     }
    32     if(pre[ed]==-1) return -1;
    33     return flow[ed];
    34 }
    35 
    36 void MFMC(int st,int ed)//max flow min cost
    37 {
    38     int k,p;
    39     fl=0,cl=0;
    40     while((k=bfs(st,ed))!=-1)
    41     {
    42         fl+=k;
    43         cl+=dis[ed]*k;
    44         p=ed;
    45         while(p!=st)
    46         {
    47             a[pre[p]].d-=k;
    48             a[pre[p]^1].d+=k;
    49             p=a[pre[p]].x;
    50         }
    51     }
    52 }
    53 
    54 费用流
    最小费用最大流

    四、最小割

    根据最大流最小割原理,最大流就是最小割。主要是建图模型。

    经典例题:一些资源,要不给A,要不给B,有相应的收益,问最大收益。写在我的题表里了。

    Tips:把下面的图拖到网页的新标签页上就可放大看了~

    我写了题解:http://www.cnblogs.com/KonjakJuruo/p/5516479.html

    五、最大费用循环流

    每条边有上下界k1、k2,费用f。问最大费用循环流。

    对于每条边(x,y,k1,k2,f),拆成:

    1.(x,y,k1,k1,f)  (再按上下界拆边,即(s,y,k1,f) (x,t,k1,f)), (x到y之间一定要流k1的流量)

    2.(y,x,k2-k1,-f)  ,   (s,y,k2-k1,0)   ,   (x,t,k2-f1,f);  (x到y之间有k2-k1的自由流,先假设全部都可以得到,然后建(y,x,k2-k1,-f)就是给它反悔的机会,如果必须反悔就减回f*流量)

    跑最大流,用k2的和判满流,满流则有解。

    六、【重点来了】下面是代码以及题表:

    代码还是见原博吧 (╯3╰)  【网络流】网络流小总结

      题目 题意 题解 备注
    poj2396 Budget 一个n*m的矩阵,给定每一行的和以及每一列的和,然后给定多个限制,即某个格子的上下界,要求一个可行的方案。 建n个点表示行,m个点表示列,原图源汇点s、t。
    s连到表示第i行的点,上下界都为sum[i](行)
    表示第i列的点连到t,上下界都为sum[i](列)
    然后表示第i行的点x和表示第j行的点y中间连一条边,上下界就是约束。

    错了很久,注意:
    1.边数要足够大
    2.p数组一开始没有清空
    3.dfs的优化(不断找,用r储存已用的流量) 快超级多。
    上下界网络流
    可行流
    矩阵模型
    模板题
    LA5095 Transportation 一个n个点、m条边的有向图,1为起点,n为终点,要从起点运k个物体到终点,每条边的费用与流量关系:f=ax^2,问最小费用。 重新开一个源点,源点到1有一条流量为k、费用为0的边,然后跑最小费用最大流,如果最大流等于k则可行。 最小费用最大流
    f=ax^i(i为自然数)模型
    模板题
    LA3487 Duopoly A、B两个公司要买一些资源(他们自己买的资源不会重复),一个资源只能卖给一个公司。问最大收益。     方法一 把每个询问看成一个点,然后A的询问连源点,B的询问连汇点,如果AB间的某个询问有矛盾就在它们中间连一条无限大的边,ans=sum-最小割。
        方法二:对于每个询问,新建一个点x,如果是A就源点连向这个点,流量为价钱p,然后连向这个询问所要求买的资源c[i],流量为INF。
    如果是B则反过来,连向汇点。
    最小割
    一个费用/流量对应多个点
    模板题
    LA5131 Chips Challenge 给定一个矩阵,每个格子有三种情况——不能填、能填、一定要填,要求填最多的格子,使第i行的总和等于第i列的总和,并且填的格子数不能大于总数的A/B。 构图:在表示第i行的点x和表示第i列的点y间连一条(y,x)的边,费用为1,然后跑最大费用流。
    详见博客。
    最大费用循环流
    模板题
    LA2796 Concert Hall Scheduling 你有2个房间,有365天,有n个人找你租房,第i个人要从第xi到第yi天要一个房(任意一个),付wi的钱,求怎样安排收的钱最多     建365个点,连(i,i+1,2,0)的边(流量2(2个房间),费用0),源点连1,流量2费用0,365连汇点,流量2费用0。然后对于一个询问(xi,yi,wi),连(xi,yi+1,2,wi)的边,注意是yi+1,不然yi这个点可能同时作为别人的起点,然后重复就WA了。跑一遍最大费用流。 最大费用流
    区间模型
    uva1515 Pool construction 给一个m*n的矩阵,每个格子中是#和.两个符号之一,分别代表草和洞。现在要将洞给围起来(将草和洞分离),每条边需花费b元(即将一个洞包起来需要4边,将2个连续的洞包起来需要6边,省了2条边)。有个特殊能力,能将洞变成草,花费f。当然也能将草变成洞,花费d。围起洞来需要多少花费。矩阵四周最边边的格子都必须是草,即出现洞就必须转草。 s代表草,t代表洞;
    对于每个点x,如果是草,建边(s,x,0),(x,t,d);
    如果是洞,建边(s,x,f),(x,t,0) 
    如果一定要选,那到另一个的流量就是INF(代价无穷大)

    对于每对相邻的点(x,y),建(x,y,b),(y,x,b)
    这样就可以保证当x、y是不同的东西,最小割的时候就必须加上b的代价,那就是洞包起来的代价。
    二分图模型
    最小割
    LA2197 Paint the Roads n个点m条边的带权有向图,现在要将某些边涂上颜色,使得每个点恰好在k个有颜色的环上,总费用最小。 题意->每个点每一秒流入和流出的流量都是k->最小费用循环流
    把一个点拆成两个点,这两个点之间连一条边,上下界都是k,然后跑有上下界的最小费用循环流。
    对于每条边(x,y,k1,k2,f),拆成:(x,y,k1,k1,f)(再按上下界拆边,即(s,y,k1,f) (x,t,k1,f)),(y,x,k2-k1,-f),(s,y,k2-k1,0),(x,t,k2-f1,f),用k2的和判满流。
    最小费用循环流
    拆点
    hdu3081 Marriage Match II n个女生与n个男生配对,每个女生只能配对某些男生,有些女生相互是朋友,每个女生也可以跟她朋友能配对的男生配对。

    每次配对,每个女生都要跟不同的男生配对且每个女生都能配到对。问最多能配对几轮。(n<=100)
    二分答案k,用并查集建边,每对可配对的男生与女生之间连一条流量为1的边,源点到每个女生连一条k的边,汇点连每个男生,流量为k。跑最大流。
    WA了一中午,对拍都看不出错,然后是一个i打成了x,泪目。
    最大流
    二分
    uva10735 Euler Circuit 给定一个混合图(有有向边和无向边),然后要你求一条欧拉回路(遍历所有边后回到出发点)。 1.欧拉回路建图,给无向边定向。最终要是in[i]==out[i],那就先给无向边随便定向,d[i]=in[i]-out[i],若d[i]为奇数则无解(反一条边,d[i]会变化2)。
      对于d[i]>0,则最多要改d[i]/2条入边,(i,t,d[i]/2)
      对于d[i]<0,则最多要该(-d[i])/2条出边,(s,i,(-d[i])/2)
      每条无向边最多更改一次,(x,y,1)
      跑最大流,满流则有解。

    2.输出欧拉回路(套圈算法)。随便从一个点开始遍历,走出一个圈,但是有一些边可能还没走,又有一个圈。做法就是起点开始遍历,dfs遍历其相邻的点,如果一个点没有相邻点了就压到栈里。倒序输出。

    最大流
    欧拉回路
    构图

             
           
             
             
             
             
             
             
             
           
  • 相关阅读:
    【BZOJ-1060】时态同步 树形DP (DFS爆搜)
    【BZOJ-1468】Tree 树分治
    【BZOJ-1097】旅游景点atr SPFA + 状压DP
    【BZOJ-3876】支线剧情 有上下界的网络流(有下界有源有汇最小费用流)
    【BZOJ-2502】清理雪道 有上下界的网络流(有下界的最小流)
    【BZOJ-2055】80人环游世界 上下界费用流 (无源无汇最小费用流)
    【BZOJ-3275&3158】Number&千钧一发 最小割
    【BZOJ-4562】食物链 记忆化搜索(拓扑序 + DP)
    【BZOJ-1367】sequence 可并堆+中位数
    【BZOJ-1455】罗马游戏 可并堆 (左偏树)
  • 原文地址:https://www.cnblogs.com/konjak/p/5935432.html
Copyright © 2011-2022 走看看