网络流(关键边&判定)


网络流(关键边&判定)


关键边

我们把增加一条路径的流量,可以增大最大流的流量,那么我们就可以把这条边称为关键边,在做完一次dinic之后如何在残留网络中去判定是否是关键边呢?首先一条关键边的流量肯定是流满的,不然就不需要增加流量了,其次可能存在路径有2条以上流满的情况,因此也要排除这种可能,第三种是判定在不同的最大可行流中保证关键边是一致的,我们可以用割去证明,如果存在那么肯定存在增广路径,所以关键边是确定的。我们可以先求一次dinic,判断边如果流满了,同时S可以到边左端点,T可到右端点,那么就是一条关键边。

#include<iostream>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int N=510,M=1e4+10;
int n,m,S,T;
int head[N],idx,q[N],d[N],cur[N];
bool vis_s[N],vis_t[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;
}
void dfs(int u,bool st[],int t)
{
    st[u]=true;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int j=i^t,ver=edge[i].to;
        if(edge[j].f&&!st[ver])
            dfs(ver,st,t);
    }
}
int main()
{
    memset(head,-1,sizeof head);
    memset(vis_s,false,sizeof vis_s);
    memset(vis_t,false,sizeof vis_t);
    scanf("%d%d",&n,&m);
    S=0,T=n-1;
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    dinic();
    dfs(S,vis_s,0);
    dfs(T,vis_t,1);
    int res=0;
    for(int i=0;i<idx;i+=2)
        if(!edge[i].f&&vis_s[edge[i^1].to]&&vis_t[edge[i].to])
            res++;
    printf("%d\n",res);
    return 0;
}

最大流判定

最大流判定一般和二分枚举倍增结合起来,根据增加残留网络或者重改网络情况来进行最大流的判定。比如在一个有权图中选择若干的边,同时每条边可以用一次,选择边权最小的边使得图可以从S走N次到T。思路是进行边权的二分,根据边权进行二分,然后建图,做一个lower_bound。

#include<iostream>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int N=210,M=1e5;
int n,m,k,S,T;
int head[N],idx;
int q[N],d[N],cur[N];
struct Edge{
    int next,to,len,f;
}edge[M];
void add(int a,int b,int c)
{
    edge[idx]=(Edge){head[a],b,c,0},head[a]=idx++;
    edge[idx]=(Edge){head[b],a,c,0},head[b]=idx++;
}
bool bfs()
{
    int hh=0,tt=0;
    memset(d,-1,sizeof d);
    q[0]=S,cur[S]=head[S],d[S]=0;
    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)
            {
                cur[ver]=head[ver];
                d[ver]=d[t]+1;
                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(limit-flow,edge[i].f));
            if(!t)  d[ver]=-1;
            edge[i].f-=t,edge[i^1].f+=t,flow+=t;
        }
    }
    return flow;
}
int dinic()
{
    int flow,res=0;
    while(bfs())
        while(flow=find(S,inf))
            res+=flow;
    return res;
}
bool check(int mid)
{
    for(int i=0;i<idx;i++)
        if(edge[i].len<=mid)    edge[i].f=1;
        else    edge[i].f=0;
    return dinic()>=k;
}
int main()
{
    memset(head,-1,sizeof head);
    scanf("%d%d%d",&n,&m,&k);
    S=1,T=n;
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    int L=1,R=1e6;
    while(L<R)
    {
        int mid=L+R>>1;
        if(check(mid))  R=mid;
        else    L=mid+1;
    }
    printf("%d\n",L);
    return 0;
}


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