网络流
网络流
最大流最小割
Ford-Fulkerson算法
用dfs实现的FF算法时间复杂度上界是 O ( m f ) O(mf) O(mf) ,其中 m m m为边数, f f f为最大流:
int n, m, s, t; // s是源点,t是汇点
bool vis[MAXN];
int dfs(int p = s, int flow = INF)//每次找一条增广路径
{
if (p == t)
return flow; // 到达终点,返回这条增广路的流量
vis[p] = true;
for (int eg = head[p]; eg; eg = edges[eg].next)
{
int to = edges[eg].to, vol = edges[eg].w, c;
// 返回的条件是残余容量大于0、未访问过该点且接下来可以达到终点(递归地实现)
// 传递下去的流量是边的容量与当前流量中的较小值
if (vol > 0 && !vis[to] && (c = dfs(to, min(vol, flow))) != -1)
{
edges[eg].w -= c;
edges[eg ^ 1].w += c;
// 这是链式前向星取反向边的一种简易的方法
// 建图时要把cnt置为1,且要保证反向边紧接着正向边建立
return c;
}
}
return -1; // 无法到达终点
}
inline int FF()
{
int ans = 0, c;
while ((c = dfs()) != -1)
{
memset(vis, 0, sizeof(vis));
ans += c;
}
return ans;
}
Edmond-Karp算法
可以算是bfs实现的FF算法,复杂度上限是 O ( n m 2 ) O(nm^2) O(nm2)
int n, m, s, t, last[MAXN], flow[MAXN];
inline int bfs()
{
memset(last, -1, sizeof(last));
queue<int> q;
q.push(s);
flow[s] = INF;
while (!q.empty())
{
int p = q.front();
q.pop();
if (p == t) // 到达汇点,结束搜索
break;
for (int eg = head[p]; eg; eg = edges[eg].next)
{
int to = edges[eg].to, vol = edges[eg].w;
if (vol > 0 && last[to] == -1) // 如果残余容量大于0且未访问过(所以last保持在-1)
{
last[to] = eg;
flow[to] = min(flow[p], vol);
q.push(to);
}
}
}
return last[t] != -1;
}
inline int EK()
{
int maxflow = 0;
while (bfs())
{
maxflow += flow[t];
for (int i = t; i != s; i = edges[last[i] ^ 1].to) // 从汇点原路返回更新残余容量
{
edges[last[i]].w -= flow[t];
edges[last[i] ^ 1].w += flow[t];
}
}
return maxflow;
}
Dinic算法
先用BFS分层,再用DFS寻找。它的时间复杂度上界是 O ( m n 2 ) O(mn^2) O(mn2)
int n, m, s, t, lv[MAXN], cur[MAXN]; // lv是每个点的层数(距源点的距离),cur用于当前弧优化标记增广起点
inline bool bfs() // BFS分层
{
memset(lv, -1, sizeof(lv));
lv[s] = 0;
memcpy(cur, head, sizeof(head)); // 当前弧优化初始化
queue<int> q;
q.push(s);
while (!q.empty())
{
int p = q.front();
q.pop();
for (int eg = head[p]; eg; eg = edges[eg].next)
{
int to = edges[eg].to, vol = edges[eg].w;
if (vol > 0 && lv[to] == -1)
lv[to] = lv[p] + 1, q.push(to);
}
}
return lv[t] != -1; // 如果汇点未访问过说明已经无法达到汇点,此时返回false
}
int dfs(int p = s, int flow = INF)
{
if (p == t)
return flow;
int rmn = flow; // 剩余的流量
for (int eg = cur[p]; eg && rmn; eg = edges[eg].next) // 如果已经没有剩余流量则退出
{
cur[p] = eg; // 当前弧优化,更新当前弧
int to = edges[eg].to, vol = edges[eg].w;
if (vol > 0 && lv[to] == lv[p] + 1) // 往层数高的方向增广
{
int c = dfs(to, min(vol, rmn)); // 尽可能多地传递流量
rmn -= c; // 剩余流量减少
edges[eg].w -= c; // 更新残余容量
edges[eg ^ 1].w += c; // 再次提醒,链式前向星的cnt需要初始化为1(或-1)才能这样求反向边
}
}
return flow - rmn; // 返回传递出去的流量的大小
}
inline int dinic()
{
int ans = 0;
while (bfs())
ans += dfs();
return ans;
}
ISAP
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=1009;
int pre[maxn],gap[maxn],h[maxn],cur[maxn];//pre记录是从哪一条边到达i的,gap记录在i层中有几个点,h记录i点的层次,cur当前弧优化。
int firs[maxn];//邻接表建边
struct node
{
int v,flow,nex;
}edge[maxn*maxn];
int N;
void build_edge(int u,int v,int flow)//建边
{
edge[N]=(node){v,flow,firs[u]};
firs[u]=N++;
edge[N]=(node){u,0,firs[v]};
firs[v]=N++;
}
void bfs(int s,int t)//广搜建层,不过这里是从汇点开始的,别弄错了,
{
//初始化
memset(h,-1,sizeof(h));
memset(gap,0,sizeof(gap));
queue<int>q;
while(!q.empty())q.pop();
q.push(t);
h[t]=0;//将汇点定义为0层
gap[0]=1;//0层点的个数加一
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=firs[u];i!=-1;i=edge[i].nex)
{
int v=edge[i].v;
if(h[v]==-1)
{
h[v]=h[u]+1;
gap[h[v]]++;//记录当前层的个数
q.push(v);
}
}
}
}
int isap(int s,int t,int n)//ISAP算法主体部分
{
bfs(s,t);
memcpy(cur,head,sizeof(head));
memset(pre,0,sizeof(pre));//记录该点的前驱是谁(这里的前驱是指边,不是点)
int ans=0,d,u=s;//ans记录最大流,d记录当前路径中的最小流,u记录当前查找到哪个点了。
while(h[s]<n)//如果源点的层小于点的个数就可能还有流
{
int flag=1;//判断是否能够走,能否找到流。
if(u==s) d=inf;//如果再次从源点出发,就初始化d,
for(int i=cur[u];i!=-1;i=edge[i].nex)
{
int v=edge[i].v,flow=edge[i].flow;
if(h[v]+1==h[u]&&flow)
{
cur[u]=i;
pre[v]=i;//记录该点是由哪条边到达的
u=v;//记录要去哪个点
d=min(d,flow);//记录最小流
flag=0;//能够往前走
if(v==t)//如果走到了汇点
{
while(u!=s)//原路返回,更新边的容量,与dinic的dfs思路一样
{
int j=pre[u];
edge[j].flow-=d;
edge[j^1].flow+=d;
u=edge[j^1].v;
}
ans+=d;//加上当前找到的流
}
break;
}
}
if(flag)//如果没有找到下面的路,就更新点的层次
{
if(--gap[h[u]]==0) //如果该层上没有了点,说明没法继续查找了,结束。
break;
int min_=n-1;
for(int i=firs[u];i!=-1;i=edge[i].nex)//查找与u相连的最小层,
{
int v=edge[i].v,flow=edge[i].flow;
if(flow)
min_=min(min_,h[v]);
}
cur[u]=head[u];
h[u]=min_+1;//重新给u建层
gap[h[u]]++;//更新层的个数
if(u!=s)//重要一步,如果当前点不是源点就要向后退一步,继续查找。
u=edge[pre[u]^1].v;
}
}
return ans;
}
int main()
{
int m,n;
while(~scanf("%d%d",&m,&n))
{
memset(firs,-1,sizeof(firs));
N=0;
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
build_edge(u,v,w);
}
printf("%d\n",isap(1,n,n));
}
return 0;
}
最小费用最大流
把最大流中的bfs改为spfa
int n, m, s, t, last[MAXN], inq[MAXN], dis[MAXN], cur[MAXN], vis[MAXN];
queue<int> Q;
bool SPFA()
{
while (!Q.empty())
Q.pop();
memcpy(cur, head, sizeof(head));
memset(last, -1, sizeof(last));
memset(inq, 0, sizeof(inq));
memset(dis, 63, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[s] = 0;
Q.push(s);
while (!Q.empty())
{
int p = Q.front();
Q.pop();
inq[p] = 0;
for (int e = head[p]; e != 0; e = edges[e].next)
{
int to = edges[e].to, vol = edges[e].w;
if (vol > 0 && dis[to] > dis[p] + edges[e].c)
{
last[to] = e;
dis[to] = dis[p] + edges[e].c;
if (!inq[to])
{
Q.push(to);
inq[to] = 1;
}
}
}
}
return last[t] != -1;
}
int dfs(int p = s, int flow = INF)
{
if (p == t)
return flow;
vis[p] = true;
int rmn = flow;
for (int eg = cur[p]; eg && rmn; eg = edges[eg].next)
{
cur[p] = eg;
int to = edges[eg].to, vol = edges[eg].w;
if (vol > 0 && !vis[to] && dis[to] == dis[p] + edges[eg].c)
{
int c = dfs(to, min(vol, rmn));
rmn -= c;
edges[eg].w -= c;
edges[eg ^ 1].w += c;
}
}
return flow - rmn;
}
int maxflow, mincost;
inline void MCMF()
{
while (SPFA())
{
int flow = dfs();
maxflow += flow;
mincost += dis[t] * flow;
}
}