Loading

网络流板子

FF算法,PP算法算是作为引入,未加任何优化

  1. FF算法

FF算法的时间复杂度与流量有关,是可以hack的。在luogu上能取得82分的好成绩

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define int long long
using namespace std;
int cnt=1,n,m,s,t,nowflow,maxflow,h[1010],vis[1010];
struct edge{int to,nxt,val;}e[10010];
void addedge(int u,int v,int val)
{
    e[++cnt]=(edge){v,h[u],val};
    h[u]=cnt;
}
int dfs(int u,int flow)
{
    if(u==t)return flow;
    vis[u]=1;
    for(int i=h[u];i;i=e[i].nxt)
    {
        h[u]=i;
        int p=e[i].to;
        if(vis[p]==1||e[i].val==0)continue;
        int nowflow=dfs(p,min(flow,e[i].val));
        if(nowflow>0)
        {
            e[i].val-=nowflow;
            e[i^1].val+=nowflow;
            return nowflow;
        }
    }
    return 0;
}
int ff()
{
    while(1)
    {
        memset(vis,0,sizeof(vis));
        nowflow=dfs(s,0x7fffffffffffffff);
        if(nowflow<=0)break;
        maxflow+=nowflow;
    }
    return maxflow;
}
signed main()
{
    scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
    for(int i=1;i<=m;i++)
    {
        int uu,vv,vval;
        scanf("%lld%lld%lld",&uu,&vv,&vval);
        addedge(uu,vv,vval);addedge(vv,uu,0);
    }
    printf("%lld\n",ff());
    return 0;
}
  1. EK算法

FF算法用的是dfs,效率极其低下。
我们把FF的dfs换成bfs,这样每次寻找的是最短的增广路径,效率高上不少。
但还是不够快,不建议写。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define int long long
using namespace std;
int n,m,s,t,cnt=1,maxflow,h[1010],inque[1010];
queue<int> q;
struct edge{int to,nxt,val;}e[10010];
struct path{int nxt,ed;}pre[1010];
void addedge(int u,int v,int val)
{
    e[++cnt]=(edge){v,h[u],val};
    h[u]=cnt;
}
bool bfs()
{
    while(!q.empty())q.pop();
    memset(inque,0,sizeof(inque));
    memset(pre,-1,sizeof(pre));
    q.push(s);inque[s]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=h[u];i;i=e[i].nxt)
        {
            int p=e[i].to;
            if(e[i].val&&!inque[p])
            {
                pre[p]=(path){u,i};
                if(p==t)return 1;
                q.push(p);inque[p]=1;
            }
        }
    }
    return 0;
}
int ek()
{
    while(bfs())
    {
        int nowflow=0x7fffffffffffffff;
        for(int i=t;i!=s;i=pre[i].nxt)nowflow=min(nowflow,e[pre[i].ed].val);
        for(int i=t;i!=s;i=pre[i].nxt)
        {
            e[pre[i].ed].val-=nowflow;
            e[pre[i].ed^1].val+=nowflow;
        }
        maxflow+=nowflow;
    }
    return maxflow;
}
signed main()
{
    scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
    for(int i=1;i<=m;i++)
    {
        int uu,vv,vval;
        scanf("%lld%lld%lld",&uu,&vv,&vval);
        addedge(uu,vv,vval);addedge(vv,uu,0);
    }
    printf("%lld\n",ek());
    return 0;
}
  1. dinic算法

主流算法。可以理解为对FF算法的一个大优化。
我们在dfs前先bfs一遍分好层,这样寻找的也是最短的增广路径。
然后我们还可以利用dfs的特性一次找多条增广路,从而大大提高了效率。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define int long long
using namespace std;
int cnt=1,n,m,s,t,maxflow,h[1010],dep[1010],inque[1010],cur[1010];
queue<int> q;
struct edge{int to,nxt,val;}e[10010];
void addedge(int u,int v,int val)
{
    e[++cnt]=(edge){v,h[u],val};
    h[u]=cnt;
}
bool bfs()
{
    memset(dep,0x7f,sizeof(dep));
    memset(inque,0,sizeof(inque));
    memcpy(cur,h,sizeof(h));
    while(!q.empty())q.pop();
    q.push(s);dep[s]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        inque[u]=0;
        for(int i=h[u];i;i=e[i].nxt)
        {
            int p=e[i].to;
            if(e[i].val&&dep[p]>dep[u]+1)
            {
                dep[p]=dep[u]+1;
                if(!inque[p])
                {
                    inque[p]=1;
                    q.push(p);
                }
            }
        }
    }
    if(dep[t]!=0x7f7f7f7f7f7f7f7f)return 1;
    return 0;
}
int dfs(int u,int flow)
{
    if(u==t)return flow;
    int used=0,nowflow=0;
    for(int i=cur[u];i;i=e[i].nxt)
    {
        cur[u]=i;
        int p=e[i].to;
        if(e[i].val&&dep[p]==dep[u]+1)
        {
            nowflow=dfs(p,min(flow-used,e[i].val));
            if(nowflow)
            {
                used+=nowflow;
                e[i].val-=nowflow;
                e[i^1].val+=nowflow;
                if(used==flow)break;
            }
        }
    }
    if(!used)dep[u]=-1;
    return used;
}
int dinic()
{
    while(bfs())maxflow+=dfs(s,0x7fffffffffffffff);
    return maxflow;
}
signed main()
{
    scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
    for(int i=1;i<=m;i++)
    {
        int uu,vv,vval;
        scanf("%lld%lld%lld",&uu,&vv,&vval);
        addedge(uu,vv,vval);addedge(vv,uu,0);
    }
    printf("%lld\n",dinic());
    return 0;
}
  1. ISAP算法

对dinic的优化。
通过一些奇怪的技巧只跑一遍bfs从而大大提高了效率。
最坏时间复杂度同dinic。
ISAP的代码长度和dinic相当甚至更短
所以也有些OI选手将ISAP作为平常使用的算法。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define int long long
using namespace std;
int cnt=1,n,m,s,t,maxflow,h[1010],dep[1010],gap[1010],cur[1010];
queue<int> q;
struct edge{int to,nxt,val;}e[10010];
void addedge(int u,int v,int val)
{
    e[++cnt]=(edge){v,h[u],val};
    h[u]=cnt;
}
void bfs()
{
    memset(dep,-1,sizeof(dep));
    queue<int> q;q.push(t);
    dep[t]=0;gap[0]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=h[u];i;i=e[i].nxt)
        {
            int p=e[i].to;
            if(dep[p]!=-1)continue;
            q.push(p);
            dep[p]=dep[u]+1;
            gap[dep[p]]++;
        }
    }
    return;
}
int dfs(int u,int flow)
{
    if(u==t)return flow;
    int used=0,nowflow=0;
    for(int i=cur[u];i;i=e[i].nxt)
    {
        cur[u]=i;
        int p=e[i].to;
        if(e[i].val&&dep[p]+1==dep[u])
        {
            nowflow=dfs(p,min(flow-used,e[i].val));
            if(nowflow)
            {
                used+=nowflow;
                e[i].val-=nowflow;
                e[i^1].val+=nowflow;
            }
            if(used==flow)return used;
        }
    }
    gap[dep[u]]--;
    if(gap[dep[u]]==0)dep[s]=n+1;
    dep[u]++;gap[dep[u]]++;
    return used;
}
int isap()
{
    bfs();
    while(dep[s]<n)
    {
        memcpy(cur,h,sizeof(h));
        maxflow+=dfs(s,0x7fffffffffffffff);
    }
    return maxflow;
}
signed main()
{
    scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
    for(int i=1;i<=m;i++)
    {
        int uu,vv,vval;
        scanf("%lld%lld%lld",&uu,&vv,&vval);
        addedge(uu,vv,vval);addedge(vv,uu,0);
    }
    printf("%lld\n",isap());
    return 0;
}
  1. PP算法

抽象的预流推进算法,虽然效率很低但还是过了板子。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define int long long
using namespace std;
const int inf=0x7fffffffffffffff;
int n,m,s,t,cnt=1,h[1010],inque[1010],height[1010],resflow[1010];
queue<int> q;
struct edge{int to,nxt,val;}e[10010];
void addedge(int u,int v,int val)
{
    e[++cnt]=(edge){v,h[u],val};
    h[u]=cnt;
}
void push(int u,int i)
{
    int nowflow=min(resflow[u],e[i].val);
    e[i].val-=nowflow;
    e[i^1].val+=nowflow;
    resflow[u]-=nowflow;
    resflow[e[i].to]+=nowflow;
    return;
}
bool relabel(int u)
{
    int minheight=inf;
    for(int i=h[u];i;i=e[i].nxt)
        if(e[i].val)
            minheight=min(minheight,height[e[i].to]);
    if(minheight==inf)return 0;
    height[u]=minheight+1;
    return 1;
}
int preflow_push()
{
    height[s]=n;
    for(int i=h[s];i;i=e[i].nxt)
    {
        int p=e[i].to,val=e[i].val;
        if(val)
        {
            e[i].val-=val;
            e[i^1].val+=val;
            resflow[s]-=val;
            resflow[p]+=val;
            if(!inque[p]&&p!=t&&p!=s&&relabel(p))
            {
                q.push(p);
                inque[p]=1;
            }
        }
    }
    while(!q.empty())
    {
        int u=q.front();q.pop();
        inque[u]=0;
        for(int i=h[u];i;i=e[i].nxt)
        {
            int p=e[i].to;
            if(resflow[u]==0)break;
            if(!e[i].val)continue;
            if(height[u]==height[p]+1)push(u,i);
            if(!inque[p]&&resflow[p]!=0&&p!=t&&p!=s&&relabel(p))
            {
                q.push(p);
                inque[p]=1;
            }
        }
        if(resflow[u]&&inque[u]==0&&relabel(u))
        {
            q.push(u);
            inque[u]=1;
        }
    }
    return resflow[t];
}
signed main()
{
    scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
    for(int i=1;i<=m;i++)
    {
        int uu,vv,vval;
        scanf("%lld%lld%lld",&uu,&vv,&vval);
        addedge(uu,vv,vval);addedge(vv,uu,0);
    }
    printf("%lld\n",preflow_push());
    return 0;
}
  1. HLPP算法

每次推流都推的是高度最高的顶点,从而大大提高了效率。

(咕咕咕)

posted @ 2021-01-24 19:36  pjykk  阅读(85)  评论(0编辑  收藏  举报