码迷,mamicode.com
首页 > 其他好文 > 详细

网络流总结

时间:2018-02-03 23:09:37      阅读:250      评论:0      收藏:0      [点我收藏+]

标签:状态   返回   cost   pos   组成   地址   总数   拍照   数字   

网络流

Tags:算法

更好阅读体验:https://www.zybuluo.com/xzyxzy/note/992041


一、基本内容

>inic基本内容

SYC:http://www.cnblogs.com/SYCstudio/p/7260613.html
朴素DFS->局限性->反悔边->低效->分层图->当前弧优化->Dinic!

>费用流基本内容

本宝宝自己写:
1.在每一条边上有单位流量的费用,你要保证流量最大的前提,费用最小
2.算法由最朴素的算法该进,便是在BFS求增广路的时候以费用为关键字跑SPFA
3.看代码

>上下界网络流

留坑

>最长反链覆盖

留坑


二、题目

1、练基础

P3376 【模板】网络最大流 https://www.luogu.org/problemnew/show/3376
P3381 【模板】最小费用最大流 https://www.luogu.org/problemnew/show/3381
H1369 网络流一 http://hihocoder.com/problemset/problem/1369
H1378 网络流二 http://hihocoder.com/problemset/problem/1378
H1393 网络流三 http://hihocoder.com/problemset/problem/1393
H1394 网络流四 http://hihocoder.com/problemset/problem/1394
H1398 网络流五 http://hihocoder.com/problemset/problem/1398
P1231 教辅的组成 https://www.luogu.org/problemnew/show/P1231
P1343 地震逃生 https://www.luogu.org/problemnew/show/1343
P2153 [SDOI2009]晨跑 https://www.luogu.org/problemnew/show/2153
P3440 [POI2006]SZK-Schools https://www.luogu.org/problemnew/show/P3440

2、刷提高

P2472 [SCOI2007]蜥蜴 https://www.luogu.org/problemnew/show/2472
P2944 [USACO]地震损失2 https://www.luogu.org/problemnew/show/2944
P1345 [USACO]奶牛的电信 https://www.luogu.org/problemnew/show/1345
P2711 小行星 https://www.luogu.org/problemnew/show/P2711
P2469 [SDOI2010]星际竞速 https://www.luogu.org/problemnew/show/2469
P2057 善意的投票 https://www.luogu.org/problemnew/show/2057
P3410 拍照 https://www.luogu.org/problemnew/show/3410

3、网络流24题

已完成

P2763 试题库问题 https://www.luogu.org/problemnew/show/2763
P3254 圆桌问题 https://www.luogu.org/problemnew/show/3254
P4013 数字梯形问题 https://www.luogu.org/problemnew/show/4013
P4016 负载平衡问题 https://www.luogu.org/problemnew/show/4016
P2756 飞行员配对方案问题 https://daniu.luogu.org/problemnew/show/2756
P3355 骑士共存问题 https://www.luogu.org/problemnew/show/3355
P2774 方格取数问题 https://www.luogu.org/problemnew/show/2774
P2765 魔术球问题 https://www.luogu.org/problemnew/show/2765
P2764 最小路径覆盖问题 https://www.luogu.org/problemnew/show/2764
P4014 分配问题 https://www.luogu.org/problemnew/show/4014
P2762 太空飞行计划问题 https://www.luogu.org/problemnew/show/2762
P2766 最长不下降子序列问题 https://www.luogu.org/problemnew/show/2766
P1251 餐巾计划问题 https://www.luogu.org/problemnew/show/1251

未完成/不可做

4、考试题

2018.1.3 团圆(费用流)
2018.1.5 变量(最小割)


三、做题经验

Part 1 易打错

1、当前弧优化的循环重点别弄错

while(BFS())
{
    for(int i=1;i<=cnt;i++)cur[i]=head[i];
    while(int tmp=DFS(S,INF))ans+=tmp;
}
for(int i=1;i<=点数;i++)cur[i]=head[i];

题目来源:试题库问题P2763

2、源点的deep一定要是1 不然跑不出来

    deep[S]=0;Q.push(S);
    deep[S]=1;Q.push(S);

3、跑最大费用最大流时,dis初值为-inf

for(int i=0;i<=T;i++)dis[i]=-10000;
for(int i=0;i<=T;i++)dis[i]=-1;

因为有-cost的边所以-1会出错
题目来源:分配问题P4014

4、最大流,费用流都可以跑环

最大流分层图可以跑环,费用流SPFA也可以


Part 2 技巧

1、拆点连边技巧

蜥蜴:https://www.luogu.org/problemnew/show/P2472
为了限制一个点的流量,将它拆成两个点并连边

有向边的时候一条边为w,cost,反过来为0,-cost
无向边正反都是w

2、记录匹配方案

满流的边便是匹配上了
P2763 试题库问题 https://www.zybuluo.com/mdeditor#980353
P2756 飞行员配对方案问题 https://daniu.luogu.org/problemnew/show/2756

3、某边改变时

并不需要重新建图
A、判断某条边(u->v)属不属于割集,那么在残余网络中u到达不了v
B、对于一条边扩容,那么在残余网络上找增广路就好了
C、减少一条边的容量,在残余网络中找是否有该容量的路,再分情况更新
例:团圆(考试题),看某一条边是不是必须要的,重新跑SPFA即可

4、最小割问题

最小割:
割是网络中定点的一个划分,它把网络中的所有顶点划分成两个顶点集合S和T,其中源点s∈S,汇点t∈T。记为CUT(S,T),满足条件的从S到T的最小的割

百度告诉我们,最小割等于最大流
但是求的是割点的时候呢,哈哈,死掉了吧————奶牛的电信
这些最小割真的哦心【你猜呀!】【你阅读了吗?】【死掉了吧~

5、有关术语

参考博客:https://www.cnblogs.com/jianglangcaijin/p/6035945.html

最小顶点覆盖
能覆盖所有的边的最少顶点数(或是最小点权和)

计算方法:最小顶点覆盖=最大匹配数

最大独立集
两两互不相邻的点组成的集合的最大点数(或是最大点权和)

计算方法:最大独立集=点总数-最小顶点覆盖
例题剖析——方格取数问题
和小行星有点类似,小行星是x,y,z要消掉一个使得消掉行星,便是S-x-y-z-T的流不存在,即求最小割,转化为最大流;此题把方格化为黑白点,便不存在 S->黑点->白点->T的流,顺次进行连边求最小割即最大流即可,注意边权
又用转化思想,化为黑白点后可以看做二分图,便成了最大独立集,为总点数-最小顶点覆盖(最大匹配/最小割)

最小路径覆盖
能覆盖所有点的最少边数(边权之和)
友链:http://blog.csdn.net/tramp_1/article/details/52742572

计算方法:二分图最小路径覆盖=一侧点数-最大匹配数
运用:常用来解决不重不漏经过图中所有点的问题,一般化u为u和u‘,对于边(u,v),link(u,v‘),最大匹配数在网络流中就是最小割/最大流,看看这题
理解:最差每个点就是一条路径,每连一条边(一个匹配)就少一条路径,n-匹配数
ps:自己习惯于跑费用流,把点拆成上下两层后这样搞:
  link(S,u,1,0); link(S,u‘,1,1); link(u‘,T,1,0); link(u,v‘,1,0);

最大权闭合子图
给定一个有向图,从中选择一些点组成一个点集V。对于V中任意一个点,其后续节点都仍然在V中

计算方法:最大权=所有正点和-最小割
或者说是最大收益问题【模板1】【模板2】【模板3


Part 3 转化思想

1、最小割思想

很多情况下,对于网络中的每个点存在有两种状态,选或不选同意或反对等等
就将它们分成两个集合S,T表示两种对立的状态,而且点与点之间也有相互的关系
改变某点的状态需要一定代价,并因此连边。

举个例子
最大权闭合子图中,假设所有实验都选,获得收益,那么最终答案是总收益-总损失,损失表示买一个器材的代价或者放弃一个实验的损失
每个实验和器材存在有选或不选的关系,假设选为S,不选为T,那么把实验与S连边,器材与T连边后,损失成了图的最小割,最小割的割集一定由与S/T相连的边组成(中间为正无穷)。当实验不选的时候,相当于割掉其与S的边,造成其边流量的损失,计算到最小割里;当器材要选的时候,相当于割掉其与T的边,造成其边流量的代价买器材,计算到最小割里。

边容量
一般呢,哪两个点不可割就连inf,x连到S会产生w贡献那么连x到T,容量为w

2、上升序列思想

主要是这题,求某一长度的上升序列的个数,那么转化成最大流,DP求出转移后,每个点只能向该点转移到的点连边(并不是比它大就能连边),所以一条流经过的路径数就是上升序列的长度。


最大流模板

//https://www.luogu.org/problemnew/show/3376
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
const int MAXN=10010;
const int MAXM=100010;
int N,M,S,T,ans,deep[MAXN];
int head[MAXN],cnt=1,cur[MAXN];
struct edge{int next,to,w;}a[MAXM<<1];
void link(int x,int y,int w){a[++cnt]=(edge){head[x],y,w};head[x]=cnt;}
int read();
queue<int>Q;
int BFS()//分好层
{
    memset(deep,0,sizeof(deep));
    deep[S]=1;Q.push(S);
    while(!Q.empty())
    {
        int now=Q.front();Q.pop();
        for(int i=head[now];i!=-1;i=a[i].next)
            if(!deep[a[i].to]&&a[i].w)
            {deep[a[i].to]=deep[now]+1;Q.push(a[i].to);}
    }//给每个深度编号
    return deep[T];
}
int DFS(int x,int flow)//在该层寻找所有增广路
{
    if(x==T)return flow;//返回最大流
    for(int &i=cur[x];i!=-1;i=a[i].next)
        //&这个符号,表示i去到cur[x]的地址,那么首先i=cur[x]然后随着i的改变cur[x]跟着改变
    {
        if(!a[i].w||deep[a[i].to]!=deep[x]+1)continue;//要求有流且在下一层
        int tmp=DFS(a[i].to,min(a[i].w,flow));
        if(tmp){a[i].w-=tmp;a[i^1].w+=tmp;return tmp;}
        //记得加反悔边,return tmp表示找到一条流就退出,是单路增广
    }
    return 0;
}
int main()
{
    //Part 1 输入建图
    N=read();M=read();
    S=read();T=read();
    memset(head,-1,sizeof(head));
    for(int i=1;i<=M;i++)
    {
        int x=read(),y=read(),w=read();
        link(x,y,w);link(y,x,0);//建边的时候要建立反悔边
    }
    //Part 2 逐层找增广路
    while(BFS())//走的到终点即存在增广路
    {
        for(int i=1;i<=N;i++)cur[i]=head[i];//当前弧优化
        while(int tmp=DFS(S,10000000))ans+=tmp;//加上增广的最大流
    }
    printf("%d\n",ans);
    return 0;
}
int read()
{
    char ch=getchar();
    int h=0;
    while(ch>‘9‘||ch<‘0‘)ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘){h=h*10+ch-‘0‘;ch=getchar();}
    return h;
}
/*
  网络流Dinic
  1.流程:
    A.输边建图(记得加上反悔边)
    B.分层跑增广路
    C.重复B直到从源点走不到汇点
  2.理解:
    A.反悔边:详见SYC博客http://www.cnblogs.com/SYCstudio/p/7260613.html
    B.如何说明分层跑是对的或者说如何跑的:
      a.每一层跑完后,S一定会到不了T
      b.那么在deep[T]步走不到T,那么要走到T,必须加上之前连接同一层的两个点,deep[T]至少加1
    C.复杂度:
      分层M,至少分N次,DFS一次是N,然后一次至少有一条边流满即剩余流量为0,视为删掉一条边
      然后也许有M次所以复杂度为O(N*N*M)
      然而实际上复杂度要小的多,这道题10000*10000*100000可是在0.05ms内跑过的
    D.当前弧优化:
      每次分好层之后,对于点P,有流进P的边和从P流出的边
      如果这条边流出满了,下次就不要搜这条边,用cur记录(没流满就return了)
 */

费用流模板

//https://www.luogu.org/problemnew/show/P3381
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
int read()
{
    char ch=getchar();
    int h=0;
    while(ch>‘9‘||ch<‘0‘)ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘){h=h*10+ch-‘0‘;ch=getchar();}
    return h;
}
const int MAXN=5001;
int N,M,S,T,head[MAXN],cnt=1,anssum=0,anscost=0;
struct edge{int next,to,w,cost;}a[MAXN*20];
void link(int x,int y,int w,int cost)
{
    a[++cnt]=(edge){head[x],y,w,cost};head[x]=cnt;
    a[++cnt]=(edge){head[y],x,0,-cost};head[y]=cnt;
}
queue<int>Q;
int dis[MAXN],e[MAXN],pe[MAXN],px[MAXN];
//pe[i]表示到达i点的那一条边的编号
//px[i]表示到达i点的前驱节点编号
bool SPFA()
{
    memset(dis,63,sizeof(dis));
    dis[S]=0;Q.push(S);
    while(!Q.empty())
    {
        int x=Q.front();
        for(int i=head[x];i!=-1;i=a[i].next)
        {
            int R=a[i].to;
            if(!a[i].w||dis[R]<=dis[x]+a[i].cost)continue;
            dis[R]=dis[x]+a[i].cost;
            pe[R]=i;px[R]=x;
            if(!e[R]){e[R]=1;Q.push(R);}
        }
        e[x]=0;Q.pop();
    }
    return dis[T]<dis[0];
    //当不存在增广路时dis[T]=dis[0]
}
int main()
{
    //init
    N=read();M=read();S=read();T=read();
    memset(head,-1,sizeof(head));
    for(int i=1;i<=M;i++)
    {
        int x=read(),y=read(),w=read(),cost=read();
        link(x,y,w,cost);
    }
    //work
    while(SPFA())//算出增广路上的流
    {
        int sum=1e9;
        for(int i=T;i!=S;i=px[i])
            sum=min(sum,a[pe[i]].w);
        anscost+=dis[T]*sum;//记录最小费用-流量×每条边单位流量费用之和
        anssum+=sum;//记录最大流
        for(int i=T;i!=S;i=px[i])
            a[pe[i]].w-=sum,a[pe[i]^1].w+=sum;//逐边减
    }
    printf("%d %d\n",anssum,anscost);
    return 0;
}

参考文献:

**SYC:http://www.cnblogs.com/SYCstudio/p/7260613.html**
ZSY:https://www.luogu.org/blog/zhoushuyu/wang-lao-liu-zong-jie

小想法:
如果以后要出题,试着来一个费用流但是不是单位流量费用而是凡有流就有费用

网络流总结

标签:状态   返回   cost   pos   组成   地址   总数   拍照   数字   

原文地址:https://www.cnblogs.com/xzyxzy/p/8410853.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!