网络流(拆点)


网络流(拆点)


概述

最大流拆点是指将图中的点进行拆分,拆成一个入点和一个出点,之间的流量设置为出入的流量限制。


三分图匹配

在二分图匹配之中在中间加入了一层图点,直接进行匹配的话会导致中间的点的流量发生问题,所以我们要限制流量。因此我们可以考虑把中间的点拆分为2个点,之间的流量为1即可,建立一个虚拟源点S到第一层点建立流量为1的边,T为最后一层,中间直接连边即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int N=410,M=5e4+10;
int n,F,D,S,T;
int head[N],d[N],cur[N],q[N],idx;
struct Edge{
    int next,to,f;
}edge[M];
void add(int a,int b,int c)
{
    edge[idx]=(Edge){head[a],b,c},head[a]=idx++;
    edge[idx]=(Edge){head[b],a,0},head[b]=idx++;
}
bool bfs()
{
    int hh=0,tt=0;
    memset(d,-1,sizeof d);
    q[0]=S,d[S]=0,cur[S]=head[S];
    while(hh<=tt)
    {
        int t=q[hh++];
        for(int i=head[t];~i;i=edge[i].next)
        {
            int ver=edge[i].to;
            if(d[ver]==-1&&edge[i].f)
            {
                d[ver]=d[t]+1;
                cur[ver]=head[ver];
                if(ver==T)  return true;
                q[++tt]=ver;
            }
        }
    }
    return false;
}
int find(int u,int limit)
{
    if(u==T)    return limit;
    int flow=0;
    for(int i=cur[u];~i&&flow<limit;i=edge[i].next)
    {
        cur[u]=i;
        int ver=edge[i].to;
        if(d[ver]==d[u]+1&&edge[i].f)
        {
            int t=find(ver,min(edge[i].f,limit-flow));
            if(!t)  d[ver]=-1;
            edge[i].f-=t;   edge[i^1].f+=t; flow+=t;
        }
    }
    return flow;
}
int dinic()
{
    int res=0,flow;
    while(bfs())
        while(flow=find(S,inf))
            res+=flow;
    return res;
}
int main()
{
    scanf("%d%d%d", &n, &F, &D);
    S = 0, T = n * 2 + F + D + 1;
    memset(head, -1, sizeof head);
    for (int i = 1; i <= F; i ++ ) add(S, n * 2 + i, 1);
    for (int i = 1; i <= D; i ++ ) add(n * 2 + F + i, T, 1);
    for (int i = 1; i <= n; i ++ )
    {
        add(i, n + i, 1);
        int a, b, t;
        scanf("%d%d", &a, &b);
        while (a -- )
        {
            scanf("%d", &t);
            add(n * 2 + t, i, 1);
        }
        while (b -- )
        {
            scanf("%d", &t);
            add(i + n, n * 2 + F + t, 1);
        }
    }
    printf("%d\n", dinic());
    return 0;
}

最长上升子序列

此处的上升子序列与之前有所不同,这里是求最长子序列的个数问题,因为是求方案我们要去存一下dp状态,所以不太适合使用LIS,直接暴力即可。然后建图的思路是从S到dp[i]==1建一条inf的边,从dp[i]==max到T建一条inf的边,从i到n+i建一条1的边限制流量,最后从n+j到i建一条流量为1的边做一个转移 。

#include<iostream>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int N=1<<10,M=3e5+10;
int n,S,T,idx;
int head[N],cur[N],d[N],q[N];
int g[N],w[N];
struct Edge{
    int next,to,f;
}edge[M];
void add(int a,int b,int c)
{
    edge[idx]=(Edge){head[a],b,c},head[a]=idx++;
    edge[idx]=(Edge){head[b],a,0},head[b]=idx++;
}
bool bfs()
{
    int hh=0,tt=0;
    memset(d,-1,sizeof d);
    q[0]=S,d[S]=0,cur[S]=head[S];
    while(hh<=tt)
    {
        int t=q[hh++];
        for(int i=head[t];~i;i=edge[i].next)
        {
            int ver=edge[i].to;
            if(d[ver]==-1&&edge[i].f)
            {
                d[ver]=d[t]+1;
                cur[ver]=head[ver];
                if(ver==T)  return true;
                q[++tt]=ver;
            }
        }
    }
    return false;
}
int find(int u,int limit)
{
    if(u==T)    return limit;
    int flow=0;
    for(int i=cur[u];~i&&flow<limit;i=edge[i].next)
    {
        cur[u]=i;
        int ver=edge[i].to;
        if(d[ver]==d[u]+1&&edge[i].f)
        {
            int t=find(ver,min(edge[i].f,limit-flow));
            if(!t)  d[ver]=-1;
            edge[i].f-=t,edge[i^1].f+=t,flow+=t;
        }
    }
    return flow;
}
int dinic()
{
    int res=0,flow;
    while(bfs())
        while(flow=find(S,inf))
            res+=flow;
    return res;
}
int main()
{
    memset(head,-1,sizeof head);
    scanf("%d",&n);
    S=0,T=2*n+1;
    for(int i=1;i<=n;i++)   scanf("%d",&w[i]);
    int s=0;
    for(int i=1;i<=n;i++){
        add(i,n+i,1);
        g[i]=1;
        for(int j=1;j<i;j++)
            if(w[j]<=w[i])  g[i]=max(g[i],g[j]+1);
        for(int j=1;j<i;j++)
            if(w[j]<=w[i]&&g[j]+1==g[i])
                add(n+j,i,1);
        s=max(s,g[i]);
        if(g[i]==1) add(S,i,1);
    }
    for(int i=1;i<=n;i++)
        if(g[i]==s) add(n+i,T,1);
    printf("%d\n",s);
    if(s==1)    printf("%d\n%d\n",n,n);
    else{
        int res=dinic();
        printf("%d\n",res);
        for(int i=0;i<idx;i+=2)
        {
            int a=edge[i^1].to,b=edge[i].to;
            if(a==S&&b==1)  edge[i].f=inf;
            else if(a==1&&b==n+1)   edge[i].f=inf;
            else if(a==n&&b==2*n)   edge[i].f=inf;
            else if(a==2*n&&b==T)   edge[i].f=inf;
        }
        res+=dinic();
        printf("%d\n",res);
    }
    return 0;
}


文章作者: Dydong
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Dydong !
  目录