首先是一些概念,容量,流量,饱和弧,非饱和弧,零弧,非零弧,增广路,残量,残量网络
1.Edmonds—Karp算法
这个方法的时间复杂度比较差 为
一直bfs找增广路,直到找不到算法结束,每次找到后,修改正向边和反向边的边权即可
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,s,t;
const int inf=pow(2,31)-1;
const int maxm=5e3+3;
const int maxn=205;
vector <int> G[maxn];
long long ans,flow;
long long dis[maxn][maxn];
int pre[maxn];
void bfs()
{
queue <int> q;
q.push(s);
flow=inf;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int k=0;k<G[u].size();k++)
{
int to=G[u][k];
if(pre[to]==-1 && dis[u][to]>0)
{
pre[to]=u;
flow=min(flow,dis[u][to]);
if(to==t) break;
q.push(to);
}
}
}
}
void EK()
{
while(1)
{
memset(pre,-1,sizeof(pre));
bfs();
if(pre[t]==-1) break;
for(int i=t;i!=s;i=pre[i])
dis[i][pre[i]]+=flow,dis[pre[i]][i]-=flow;
ans+=flow;
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&s,&t);
int x,y,z;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
G[x].push_back(y);
G[y].push_back(x);
dis[x][y]+=z;
}
EK();
printf("%lld\n",ans);
return 0;
}
//这个EK再洛谷p3376上会T两个点
2.Dinic算法
首先通过bfs分层,然后用dfs多路同时增广
时间复杂度
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,s,t;
const long long inf=1e18;
const int maxm=1e4+3;
const int maxn=205;
struct edge
{
int to,nxt;
long long v;
}e[maxm<<1];
long long ans,dis[maxn];
int head[maxn],cnt=1;
void add(int x,int y,int z)
{
e[++cnt].to=y;
e[cnt].nxt=head[x];
e[cnt].v=z;
head[x]=cnt;
}
bool bfs()
{
memset(dis,-1,sizeof(dis));
queue <int> q;
q.push(s);
dis[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt)
{
int to=e[i].to;
if(dis[to]==-1 && e[i].v>0)
{
dis[to]=dis[u]+1;
q.push(to);
}
}
}
return (dis[t]!=-1);
}
long long dfs(int x,long long flow)
{
if(x==t) return flow;
long long res=0;
for(int i=head[x];i;i=e[i].nxt)
{
int to=e[i].to;
if(e[i].v>0 && dis[to]==dis[x]+1)
{
long long tmp=dfs(to,min(flow,e[i].v));
flow-=tmp; e[i].v-=tmp;
res+=tmp; e[i^1].v+=tmp;
//if(!flow) break;
}
}
if(!res) dis[x]=-1;
return res;
}
void dinic()
{
while(bfs())
{
ans+=dfs(s,inf);
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&s,&t);
int x,y;
long long z;
for(int i=1;i<=m;i++)
{
scanf("%d%d%lld",&x,&y,&z);
add(x,y,z);
add(y,x,0);
}
dinic();
printf("%lld\n",ans);
return 0;
}
写反向边 (^1) 的时候,cnt一定要从1开始啊,查了好长时间!!!
3.最大流最小割定理
就是最大流=最小割,建模时常用
4.dfs求割集
先用dinic求出最大流,然后dfs遍历剩余网络中所有被遍历的点存在集合S中即可
5.最小费用最大流
用spfa来找增广路增广,spfa的距离就是费用
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,s,t;
const int maxn=5e3+5;
const int inf=pow(2,31)-1;
const int maxm=5e4+5;
struct edge
{
int u,to,nxt,v,w;
}e[maxm<<1];
int head[maxn],cnt=1;
void add(int x,int y,int a,int b)
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
e[cnt].u=x;
e[cnt].v=a;
e[cnt].w=b;
head[x]=cnt;
}
int vis[maxn],dis[maxn],pre[maxn];
bool spfa()
{
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
memset(pre,-1,sizeof(pre));
dis[s]=0;
queue <int> q;
q.push(s);
vis[s]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].nxt)
{
if(!e[i].v) continue;
int to=e[i].to;
if(dis[u]+e[i].w<dis[to])
{
dis[to]=dis[u]+e[i].w;
pre[to]=i;
if(!vis[to])
{
q.push(to);
vis[to]=1;
}
}
}
}
return (pre[t]!=-1);
}
void mcmf()
{
int ans=0,res=0;
while(spfa())
{
int flow=inf;
for(int i=t;i!=s;i=e[pre[i]^1].to)
flow=min(flow,e[pre[i]].v);
for(int i=t;i!=s;i=e[pre[i]^1].to)
{
e[pre[i]].v-=flow;
e[pre[i]^1].v+=flow;
res+=e[pre[i]].w*flow;
}
ans+=flow;
}
printf("%d %d\n",ans,res);
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&s,&t);
int x,y,z,w;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&x,&y,&z,&w);
add(x,y,z,w); add(y,x,0,-w);
}
mcmf();
return 0;
}