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遍历其相邻的点,如果一个点没有相邻点了就压到栈里。倒序输出。

    最大流
    欧拉回路
    构图

             
           
             
             
             
             
             
             
             
           
  • 相关阅读:
    【Codechef】Chef and Bike(二维多项式插值)
    USACO 完结的一些感想
    USACO 6.5 Checker Challenge
    USACO 6.5 The Clocks
    USACO 6.5 Betsy's Tour (插头dp)
    USACO 6.5 Closed Fences
    USACO 6.4 Electric Fences
    USACO 6.5 All Latin Squares
    USACO 6.4 The Primes
    USACO 6.4 Wisconsin Squares
  • 原文地址:https://www.cnblogs.com/konjak/p/5935432.html
Copyright © 2011-2022 走看看