网络流全纪录

网络流最明显的特征就是:既像dp,又像贪心
一些匹配向问题都可以往网络流方向想(棋盘交换,攻击伤害,工作安排,消除矛盾)

最大流
  • 正难则反,大变小,流化割

  • 黑白染色,可以解决相邻结点不形容的问题

  • 一定要有限流(人+菜+房间,人放中间限制流量)【经典例题】

  • 行列成点(缩点)

  • 时间的推移可以枚举,每次新建一层点,上一层的点连向下一层的后继点表示时间的推移

  • 二分图两部分的点一般不是一模一样的,如果是的话,我们可以考虑在中间先限流

  • 在集合中选出一些点,使得两两之间没有冲突,可以考虑最小割(“最小割最小代价消除矛盾”,集合划分问题)

  • 二分图,可以解决最大匹配,最小覆盖,所有点均经过等问题

经典例题:交换贴纸

费用流
  • 手写队列

  • 费用随次数有差别的时候,可以考虑拆边

  • 【经典模型】等待时间的累加(第i个人完成第j个任务,是第i个人完成的倒数第k个任务)
    这个问题还有进一步优化(因为每次只找一个增广路,所以我们可以增广一次连一次边)

退流

经典例题
退流,实际上就是判断并证明一条边在最大流中的必要性
只有这条边满流,而且一定在最大流中出现时(没有其他增广路能连接原边的两个端点),退流算法才会选择删除这条边

假如要删除 from>to f r o m − > t o 这条边(保证 from>to f r o m − > t o 这条边在原图中存在)
第一步就是 dfs(from,to) d f s ( f r o m , t o ) ,走残量网络判断是否有其他路径连接 from,to f r o m , t o
如果不连通,我们就可以选择删除这条边了
删边前,暴力退流: T>to,from>S T − > t o , f r o m − > S
退完流可能整个图不再满足流量平衡的限制,但是并不影响下一步的退流操作(还是可以得到正确答案的)

网络流经典模型

DINIC

const int INF=0x33333333;
const int N=100010;
struct node{
    int x,y,v,nxt;
};
node way[N<<1];
int st[N],tot=0,cur[N],deep[N];
int q[N];

void add(int u,int w,int z) {
    tot++;way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
    tot++;way[tot].x=w;way[tot].y=u;way[tot].v=0;way[tot].nxt=st[w];st[w]=tot;
}

int bfs(int s,int t) {
    int tou,wei;
    tou=wei=0;
    for (int i=s;i<=t;i++) cur[i]=st[i];
    memset(deep,-1,sizeof(deep));
    q[++wei]=s; deep[s]=1;
    while (tou<wei) {
        int now=q[++tou];
        for (int i=st[now];i!=-1;i=way[i].nxt)
            if (way[i].v&&deep[way[i].y]==-1) {
                deep[way[i].y]=deep[now]+1;
                q[++wei]=way[i].y;
            }
    }
    return deep[t]!=-1; 
}

int dfs(int now,int t,int limit) {
    if (now==t||!limit) return limit;
    int flow=0,f;
    for (int i=cur[now];i!=-1;i=way[i].nxt) {
        cur[now]=i;
        if (way[i].v&&deep[way[i].y]==deep[now]+1&&(f=dfs(way[i].y,t,min(limit,way[i].v))))
        {
            flow+=f;
            limit-=f;
            way[i].v-=f;
            way[i^1].v+=f;
            if (!limit) break;
        }
    }
    return flow;
}

int dinic(int s,int t) {
    int ans=0;
    while (bfs(s,t)) 
        ans+=dfs(s,t,INF);
    return ans;
}

void init() {
    memset(st,-1,sizeof(st));
    tot=-1;
}

费用流
注意时刻维护pre数组,以及增广的时候是在pre边上操作

const int INF=0x33333333;
const int N=100010;
struct node{
    int x,y,v,c,nxt;
};
node way[N<<1];
int st[N],tot=0,dis[N],pre[N];
int q[N];
bool in[N];

void add(int u,int w,int z,int cc) {
    tot++;way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].c=cc;way[tot].nxt=st[u];st[u]=tot;
    tot++;way[tot].x=w;way[tot].y=u;way[tot].v=0;way[tot].c=-cc;way[tot].nxt=st[w];st[w]=tot;
}

int spfa(int s,int t) {
    int tou,wei;
    tou=wei=0;
    memset(in,0,sizeof(in)); 
    memset(dis,0x33,sizeof(dis));
    q[++wei]=s;
    dis[s]=0; in[s]=1;
    while (tou<wei) {
        int now=q[++tou];
        in[now]=0;
        for (int i=st[now];i!=-1;i=way[i].nxt)
            if (way[i].v) {
                if (dis[way[i].y]>dis[now]+way[i].c) {
                    dis[way[i].y]=dis[now]+way[i].c;
                    pre[way[i].y]=i;     //注意维护转移边 
                    if (!in[way[i].y])
                        q[++wei]=way[i].y,in[way[i].y]=1;
                }
            }
    }
    return dis[t]!=INF;
}

int solve(int s,int t) {
    int ans=0;
    while (spfa(s,t)) {
        int sum=INF;
        for (int i=t;i!=s;i=way[pre[i]].x) 
            sum=min(sum,way[pre[i]].v);        //注意边的编号 
        ans=dis[t]*sum;
        for (int i=t;i!=s;i=way[pre[i]].x)
            way[pre[i]].v-=sum,
            way[pre[i]^1].v+=sum;
    }
    return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值