1 增广路算法处理最大流问题
struct Edge { int from;//起点 int to;//终点 int cap;//容量 int flow;//流量 Edge(int u, int v, int c, int f):form(u), to(v), cap(c), flow(f) {} }; struct EdomonsKarp { int n, m; vector<Edge> edges;//边数的两倍,正向+反向 vector<int> G[maxn];//邻接表,G[i][j]表示节点i的第j条边在e数组中的序号 int a[maxn];//起点到i的可改进量(残量) int p[maxn];//边i的起点在edges中的编号 void init(int n) { for (int i = 0; i < n; i ++) G[i].clear(); edges.clear(); } void addEdges(int from, int to, int cap) { edges.push_back(Edge(from, to, cap, 0)); edges.push_back(Edge(to, from, 0, 0));//反向弧 m = edges.size(); G[from].push_back(m-2);//新加入的边(from-to)是节点from的第G[from].size()条边,在edges中是第m-1条 G[to].push_back(m-1);//新加入的边(to-from)是节点to的第G[to].size()条边,在edges中是第m条 } int MaxFlow(int s, int t) { int flow = 0; for(;;) { memset(a, 0, sizeof(a)); queue<int> Q; Q.push(s); a[s] = INF; while (!Q.empty()) { int x = Q.front();//当前处理节点 Q.pop(); for(int i = 0; i < G[x].size(); i ++) { Edge &e = edges[G[x][i]];//当前节点的第i条边 if(!a[e.to] && e.cap > e.flow) {//如果起点到此边终点可改进,并且边的容量大于流量 p[e.to] = G[x][i];//此边的起点是edges的第几条边,方便最后从终点到起点检索 a[e.to] = min(a[x], e.cap-e.flow);//取之前的可改进量和当前边的可改进量最小值,为从起点到此边终点的最大可改进量 Q.push(e.to); } } if(a[t]) break;//遍历到终点 } if(!a[t]) break;//无可改进量,返回结果 for (int u = t; u !=s; u = edges[p[u]].from ) { edges[p[u]].flow += a[t];// edges[p[u]^1].flow -= a[t]; }//通过更新flow建立新的残量网络 flow += a[t];//累加本次循环的可改进量 } return flow; } }
主要思路是通过BFS遍历网络,将每次的可改进量从最优路径中删除,直到无可改进量为止。每次的可改进量累加,就是最终网络最大流
下面是Dinic算法
struct Dinic { int n, m, s, t;//节点数,边数(包括反向弧),源点编号和终点编号 vector<Edge> edges;//边表, edges[e]h和edges[e^1] 互为反向弧 vector<int> G[maxn]; bool vis[maxn]; int d[maxn];//起点到i的距离 int cur[maxn];//当前弧下标 bool BFS() { memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(s); d[s] = 0; vis[s] = 1; while(!Q.empty()) { int x = Q.front(); Q.pop(); for (int i = 0; i < G[x].size(); i ++) { Edge &e = edges[G[x][i]]; if(!vis[e.to] && e.cap >e.flow) { vis[e.to] = 1; d[e.to] = d[x] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a) { if (x == t || a == 0) return a; int flow = 0, f; for (int &i = cur[x]; i < G[x]; i ++) { Edge &e = edges[G[x][i]]; if (d[x] + 1 == d[e.to] && (f == DFS(e.to, min(a, e.cap - e.flow))) > 0) { e.flow += f; edges[G[x][i]^1].flow -= f; flow += f; a -= f; if(a == 0) break; } } return flow; } int MaxFlow(int s, int t) { this->s = s; this ->t = t; int flow = 0; while(BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(S, INF); } return flow; } }
2 Bellman-Ford算法解决最小费用最大流问题
struct Edge { int from;//起点 int to;//终点 int cap;//容量 int flow;//流量 int cost;//费用 Edge(int u, int v, int c, int f, int s):form(u), to(v), cap(c), flow(f), cost(s){} }; struct MCMF { int n, m; vector<Edge> edges;//边数的两倍,正向+反向 vector<int> G[maxn];//邻接表,G[i][j]表示节点i的第j条边在e数组中的序号 int a[maxn];//起点到i的可改进量(残量) int p[maxn];//边i的起点在edges中的编号 int d[maxn];//Bellman-Ford void init(int n) { this->n = n for (int i = 0; i < n; i ++) G[i].clear(); edges.clear(); } void addEdges(int from, int to, int cap, int cost) { edges.push_back(Edge(from, to, cap, 0, cost)); edges.push_back(Edge(to, from, 0, 0, -cost));//反向弧 m = edges.size(); G[from].push_back(m-2);//新加入的边(from-to)是节点from的第G[from].size()条边,在edges中是第m-1条 G[to].push_back(m-1);//新加入的边(to-from)是节点to的第G[to].size()条边,在edges中是第m条 } int BellmanFord(int s, int t, int &flow, long cost) { for (int i = 0; i < n; i++) d[i] = INF; memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF; int flow = 0; queue<int> Q; Q.push(s); while (!Q.empty()) { int u = Q.front();//当前处理节点 Q.pop(); inq[u] = 0; for(int i = 0; i < G[u].size(); i ++) { Edge &e = edges[G[u][i]];//当前节点的第i条边 if(d[e.to] > d[u] + e.cost && e.cap > e.flow) {//如果起点到此边终点可改进,并且边的容量大于流量 d[e.to] = d[u] + e.cost;//增加cost p[e.to] = G[x][i];//此边的起点是edges的第几条边,方便最后从终点到起点检索 a[e.to] = min(a[x], e.cap-e.flow);//取之前的可改进量和当前边的可改进量最小值,为从起点到此边终点的最大可改进量 if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } } } } if(d[t] == INF) return false;//无可改进量,返回结果 flow += a[t]; cost += (long)d[t] * (long)a[t]; for (int u = t; u !=s; u = edges[p[u]].from ) { edges[p[u]].flow += a[t];// edges[p[u]^1].flow -= a[t]; }//通过更新flow建立新的残量网络 return true; } int MincostMaxFlow(int s, int t, long &cost) { int flow = 0; int cost = 0; while(BellmanFord(s, t, flow, cost)); return flow; } }
和增广路算法不同的是需要统计每次增广算法增加的费用,保证在最大流前提下,费用最小。