网络流板子
FF算法,PP算法算是作为引入,未加任何优化
- 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;
}
- 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;
}
- 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;
}
- 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;
}
- 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;
}
- HLPP算法
每次推流都推的是高度最高的顶点,从而大大提高了效率。
(咕咕咕)