相关资料
用最通俗的语言让你学会网络流
EK不够快?再学个Dinic吧
究级的最大流算法:ISAP与HLPP
1. 最大流
基本概念:用最通俗的语言让你学会网络流
1.EK(Edmond—Karp)算法
思路十分暴力,就是建反边以便反悔,不断宽搜找可行流,然后暴力扩展,直到没有可行流为止。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=1e9;
struct Pre{
int v/*vertex*/,e/*edge*/;
}pre[MAXN];
int N,M,S,T;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
bool inq[MAXN];
inline void add(int x,int y,int z){
nxt[top]=head[x];
head[x]=top;
to[top]=y;
val[top]=z;
top++;
}
inline bool bfs(){
queue<int> que;
memset(inq,0,sizeof(inq));
memset(pre,0,sizeof(pre));
que.push(S);
inq[S]=1;
while(que.size()){
int ii=que.front();
que.pop();
for(int i=head[ii];i;i=nxt[i]){
if(!val[i]||inq[to[i]]){
continue;
}
pre[to[i]].v=ii;
pre[to[i]].e=i;
if(to[i]==T){
return 1;
}
inq[to[i]]=1;
que.push(to[i]);
}
}
return 0;
}
inline int EK(){
int ans=0;
while(bfs()){
int minv=INF;
for(int i=T;i!=S;i=pre[i].v){
minv=min(minv,val[pre[i].e]);
}
for(int i=T;i!=S;i=pre[i].v){
val[pre[i].e]-=minv;
val[pre[i].e^1]+=minv;
}
ans+=minv;
}
return ans;
}
int main(){
scanf("%d%d%d%d",&N,&M,&S,&T);
for(int i=1;i<=M;i++){
int ii,jj,kk;
scanf("%d%d%d",&ii,&jj,&kk);
add(ii,jj,kk);
add(jj,ii,0);
}
printf("%d",EK());
return 0;
}
2.Dinic算法
Dinic在EK的基础上又一定的改进,每次宽搜顺便分层,然后用深搜扩展所有可行流,如此往复。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
int inq[MAXN],de[MAXN];
inline void add(int x,int y,int z){
nxt[top]=head[x];
head[x]=top;
to[top]=y;
val[top]=z;
top++;
}
inline bool bfs(){
queue<int> que;
memset(inq,0,sizeof(inq));
memset(de,0x3f,sizeof(de));
que.push(S);
de[S]=0;
while(que.size()){
int ii=que.front();
que.pop();
inq[ii]=0;
for(int i=head[ii];i;i=nxt[i]){
if(val[i]&&de[to[i]]>de[ii]+1){
de[to[i]]=de[ii]+1;
if(!inq[to[i]]){
que.push(to[i]);
inq[to[i]]=1;
}
}
}
}
return de[T]==INF?0:1;
}
int dfs(int x,int flow){
if(x==T){
return flow;
}
for(int i=head[x];i;i=nxt[i]){
if(val[i]&&de[to[i]]==de[x]+1){
int ii=dfs(to[i],min(flow,val[i]));
if(ii){
val[i]-=ii;
val[i^1]+=ii;
return ii;
}
}
}
return 0;
}
inline int Dinic(){
int ans=0,ii;
while(bfs()){
do{
ii=dfs(S,INF);
ans+=ii;
}while(ii);
}
return ans;
}
int main(){
scanf("%d%d%d%d",&N,&M,&S,&T);
for(int i=1;i<=M;i++){
int ii,jj,kk;
scanf("%d%d%d",&ii,&jj,&kk);
add(ii,jj,kk);
add(jj,ii,0);
}
printf("%d",Dinic());
return 0;
}
另外,在Dinic的基础下,还可以进行当前弧优化,也就是说在同一次宽搜后,每一条边只访问一次,可以提升一定的效率
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
int inq[MAXN],de[MAXN],cur[MAXN];
inline void add(int x,int y,int z){
nxt[top]=head[x];
head[x]=top;
to[top]=y;
val[top]=z;
top++;
}
inline bool bfs(){
queue<int> que;
memset(inq,0,sizeof(inq));
memset(de,0x3f,sizeof(de));
memcpy(cur,head,sizeof(cur));
que.push(S);
de[S]=0;
while(que.size()){
int ii=que.front();
que.pop();
inq[ii]=0;
for(int i=head[ii];i;i=nxt[i]){
if(val[i]&&de[to[i]]>de[ii]+1){
de[to[i]]=de[ii]+1;
if(!inq[to[i]]){
que.push(to[i]);
inq[to[i]]=1;
}
}
}
}
return de[T]==INF?0:1;
}
int dfs(int x,int flow){
if(x==T){
return flow;
}
for(int i=cur[x];i;i=nxt[i]){
cur[x]=i;
if(val[i]&&de[to[i]]==de[x]+1){
int ii=dfs(to[i],min(flow,val[i]));
if(ii){
val[i]-=ii;
val[i^1]+=ii;
return ii;
}
}
}
return 0;
}
inline int Dinic(){
int ans=0,ii;
while(bfs()){
do{
ii=dfs(S,INF);
ans+=ii;
}while(ii);
}
return ans;
}
int main(){
scanf("%d%d%d%d",&N,&M,&S,&T);
for(int i=1;i<=M;i++){
int ii,jj,kk;
scanf("%d%d%d",&ii,&jj,&kk);
add(ii,jj,kk);
add(jj,ii,0);
}
printf("%d",Dinic());
return 0;
}
Dinic还可以加used优化,记录已经用过的总流量,防止找到过多流量。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T,maxflow;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
int inq[MAXN],de[MAXN],cur[MAXN];
inline void add(int x,int y,int z){
nxt[top]=head[x];
head[x]=top;
to[top]=y;
val[top]=z;
top++;
}
inline bool bfs(){
queue<int> que;
memset(inq,0,sizeof(inq));
memset(de,0x3f,sizeof(de));
memcpy(cur,head,sizeof(cur));
que.push(S);
de[S]=0;
while(que.size()){
int ii=que.front();
que.pop();
inq[ii]=0;
for(int i=head[ii];i;i=nxt[i]){
if(val[i]&&de[to[i]]>de[ii]+1){
de[to[i]]=de[ii]+1;
if(!inq[to[i]]){
que.push(to[i]);
inq[to[i]]=1;
}
}
}
}
return de[T]==INF?0:1;
}
int dfs(int x,int flow){//·µ»ØÒÑÓÃÁ÷Á¿
if(x==T){
maxflow+=flow;
return flow;
}
int used=0;
for(int i=cur[x];i;i=nxt[i]){
cur[x]=i;
if(val[i]&&de[to[i]]==de[x]+1){
int ii=dfs(to[i],min(flow-used,val[i]));//·ÀÖ¹³¬¹ýÁ÷Á¿
if(ii){
used+=ii;
val[i]-=ii;
val[i^1]+=ii;
if(used==flow){//ÂúÁ÷Á¿
break;
}
}
}
}
return used;
}
inline void Dinic(){
while(bfs()){
dfs(S,INF);
}
printf("%d",maxflow);
}
int main(){
scanf("%d%d%d%d",&N,&M,&S,&T);
for(int i=1;i<=M;i++){
int ii,jj,kk;
scanf("%d%d%d",&ii,&jj,&kk);
add(ii,jj,kk);
add(jj,ii,0);
}
Dinic();
return 0;
}
3. ISAP算法
ISAP在Dinic基础上优化,只做一次bfs,从T向S统计深度,当一个点的出流量大于入流量时将他的深度加一,出现断层时就结束。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T,maxflow;
int head[MAXN],val[MAXM*2],to[MAXM*2],nxt[MAXM*2],top=2;
int gap[MAXN],de[MAXN],cur[MAXN];
inline void add(int x,int y,int z){
nxt[top]=head[x];
head[x]=top;
to[top]=y;
val[top]=z;
top++;
}
inline void bfs(){
queue<int> que;
memset(gap,0,sizeof(gap));
memset(de,-1,sizeof(de));
que.push(T);
de[T]=0;
gap[0]=1;
while(que.size()){
int ii=que.front();
que.pop();
for(int i=head[ii];i;i=nxt[i]){
if(de[to[i]]!=-1){
continue;
}
de[to[i]]=de[ii]+1;
gap[de[to[i]]]++;
que.push(to[i]);
}
}
}
int dfs(int x,int flow){
if(x==T){
maxflow+=flow;
return flow;
}
int used=0;
for(int i=cur[x];i;i=nxt[i]){
cur[x]=i;
if(val[i]&&de[to[i]]+1==de[x]){
int ii=dfs(to[i],min(flow-used,val[i]));//·ÀÖ¹³¬¹ýÁ÷Á¿
if(ii){
used+=ii;
val[i]-=ii;
val[i^1]+=ii;
if(used==flow){
return used;
}
}
}
}
gap[de[x]]--;
if(!gap[de[x]]){
de[S]=N+1;
}
de[x]++;
gap[de[x]]++;
return used;
}
inline void ISAP(){
bfs();
while(de[S]<N){
memcpy(cur,head,sizeof(cur));
dfs(S,INF);
}
printf("%d",maxflow);
}
int main(){
scanf("%d%d%d%d",&N,&M,&S,&T);
for(int i=1;i<=M;i++){
int ii,jj,kk;
scanf("%d%d%d",&ii,&jj,&kk);
add(ii,jj,kk);
add(jj,ii,0);
}
ISAP();
return 0;
}
2.最小费用最大流
1. EK算法
将bfs改成spfa求最短路即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
struct Pre{
int e,v;
}pre[MAXN];
int N,M,S,T;
int head[MAXN],to[MAXM*2],val[MAXM*2],cost[MAXM*2],nxt[MAXM*2],tp=2;
int dis[MAXN];
bool inq[MAXN];
inline void add(int x,int y,int z,int k){
nxt[tp]=head[x];
head[x]=tp;
to[tp]=y;
val[tp]=z;
cost[tp]=k;
tp++;
}
inline bool spfa(){
queue<int> que;
memset(pre,0,sizeof(pre));
memset(dis,0x3f,sizeof(dis));
memset(inq,0,sizeof(inq));
inq[S]=1;
dis[S]=0;
que.push(S);
while(que.size()){
int ii=que.front();
que.pop();
inq[ii]=0;
for(int i=head[ii];i;i=nxt[i]){
if(val[i]&&dis[to[i]]>dis[ii]+cost[i]){
dis[to[i]]=dis[ii]+cost[i];
pre[to[i]].v=ii;
pre[to[i]].e=i;
if(!inq[to[i]]){
inq[to[i]]=1;
que.push(to[i]);
}
}
}
}
return dis[T]==INF?0:1;
}
inline void EK(){
int ans=0,tot=0;
while(spfa()){
int minv=INF;
for(int i=T;i!=S;i=pre[i].v){
minv=min(minv,val[pre[i].e]);
}
for(int i=T;i!=S;i=pre[i].v){
val[pre[i].e]-=minv;
val[pre[i].e^1]+=minv;
}
ans+=minv;
tot+=minv*dis[T];
}
printf("%d %d",ans,tot);
}
int main(){
scanf("%d%d%d%d",&N,&M,&S,&T);
for(int i=1;i<=M;i++){
int ii,jj,kk,ll;
scanf("%d%d%d%d",&ii,&jj,&kk,&ll);
add(ii,jj,kk,ll);
add(jj,ii,0,-ll);
}
EK();
return 0;
}
2. Dinic
和Dinic一样,但不可以当前弧优化
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1e4+7,MAXM=1e5+7,INF=0x3f3f3f3f;
int N,M,S,T,maxflow,totcost;
int head[MAXN],to[MAXM*2],val[MAXM*2],cost[MAXM*2],nxt[MAXM*2],tp=2;
int vis[MAXN],dis[MAXN];
inline void add(int x,int y,int z,int k){
nxt[tp]=head[x];
head[x]=tp;
to[tp]=y;
val[tp]=z;
cost[tp]=k;
tp++;
}
inline bool spfa(){
queue<int> que;
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
que.push(S);
vis[S]=1;
dis[S]=0;
while(que.size()){
int ii=que.front();
que.pop();
vis[ii]=0;
for(int i=head[ii];i;i=nxt[i]){
if(val[i]&&dis[to[i]]>dis[ii]+cost[i]){
dis[to[i]]=dis[ii]+cost[i];
if(!vis[to[i]]){
que.push(to[i]);
vis[to[i]]=1;
}
}
}
}
return dis[T]==INF?0:1;
}
int dfs(int x,int flow){
if(x==T){
maxflow+=flow;
return flow;
}
int used=0;
vis[x]=1;
for(int i=head[x];i;i=nxt[i]){
if(!vis[to[i]]&&val[i]&&dis[to[i]]==dis[x]+cost[i]){
int ii=dfs(to[i],min(flow-used,val[i]));
if(ii){
totcost+=ii*cost[i];
used+=ii;
val[i]-=ii;
val[i^1]+=ii;
if(used==flow){
break;
}
}
}
}
return used;
}
inline void Dinic(){
while(spfa()){
dfs(S,INF);
}
printf("%d %d",maxflow,totcost);
}
int main(){
scanf("%d%d%d%d",&N,&M,&S,&T);
for(int i=1;i<=M;i++){
int ii,jj,kk,ll;
scanf("%d%d%d%d",&ii,&jj,&kk,&ll);
add(ii,jj,kk,ll);
add(jj,ii,0,-ll);
}
Dinic();
return 0;
}