在增广路径的过程中每次进行增广操作之后,得到的新图称为旧图的残余网络。

1. Fold-Fulkerson算法

 
    每用DFS执行一次找路径,增广的操作,都会使得最大流增加,假设最大流为C,那么时间复杂度可以达到 C*(m+n), m为边的数目,n为顶点的数目。

2. Edmonds-Karp算法

 
    时间复杂度可以达到 n*m*m, n为顶点的数目,m为边的数目。

EdmondsKarp算法的实现

//增广操作
int Augment(int s, int t, int n){
    memset(gVisited, false, sizeof(gVisited));
    gVisited[s] = true;
    int find_path = false;
    queue<int>Q;
    Q.push(s);
    while (!Q.empty()){
        int u = Q.front();
        Q.pop();
        //广度优先搜索一条从源点到汇点的路径
        for (int i = 0; i < n; i++){
            if (gGraph[u][i] > 0 && !gVisited[i]){
                gVisited[i] = true;
                gPre[i] = u;
                if (i == t){
                    find_path = true;
                    break;
                }
                Q.push(i);
            }
        }
    }
    if (!find_path)
        return 0;
    int min_flow = 1 << 28;
    int u = t;
    //寻找路径上最小的容量
    while (u != s){
        int v = gPre[u];
        min_flow = min_flow < gGraph[v][u] ? min_flow : gGraph[v][u];
        u = v;
    }
    u = t;
    //进行增广操作
    while (u != s){
        int v = gPre[u];
        gGraph[v][u] -= min_flow;
        gGraph[u][v] += min_flow;
        u = v;
    }
    return min_flow;
}
int main(){
    //buildgraph
    int aug;
    int max_flow = 0;
    while (aug = Augment(s, t, n)){
        max_flow += aug;
    }

    return 0;
}

 

3. Dinic算法

 
4.路增广操作之后进行回溯,将点u回溯到点A,使得从源点到点A的路径上流量不为0,且点A尽可能靠近汇点径5. 从点u开始继续选择可行点v(满足 dist[v] = dist[u] + 1),直到汇点,这样就又找到一条增广路径....
6. 直到点u回溯到源点,再回到1继续操作,直到在分层操作时,无法用BFS找到从源到汇的路径。

 

Dinic 算法的实现

bool gVisited[N];
int gLayer[N];
int gGraph[N][N];
//将节点进行分层
bool CountLayer(int s, int t, int n){
    deque<int> dQ;
    memset(gLayer, 0xFF, sizeof(gLayer));
    dQ.push_back(s);
    gLayer[s] = 0;
    while (!dQ.empty()){
        int v = dQ.front();
        dQ.pop_front();
        for (int i = 0; i < n; i++){
            if (gLayer[i] == -1 && gGraph[v][i] > 0){
                gLayer[i] = gLayer[v] + 1;
                if (i == t)
                    return true;
                dQ.push_back(i);
            }            
        }
    }
    return false;
}
int Dinic(int s, int t, int n){
    int max_flow = 0;
    deque<int> dQ;
    while (CountLayer(s, t, n)){
        dQ.push_back(s);
        memset(gVisited, false, sizeof(gVisited));
        gVisited[s] = true;
        while (!dQ.empty()){
            int v = dQ.front();
            dQ.pop_front();
            if (v == t){
                int min_flow = 1 << 29;
                int min_vs = 0;
                for (int i = 0; i < dQ.size() - 1; i++){
                    int vs = dQ[i];
                    int ve = dQ[i + 1];
                    if (min_flow > gGraph[vs][ve]){
                        min_flow = gGraph[vs][ve];
                        min_vs = vs;
                    }
                }
                //增广路径
                max_flow += min_flow;
                for (int i = 0; i < dQ.size() - 1; i++){
                    int vs = dQ[i];
                    int ve = dQ[i + 1];
                    gGraph[vs][ve] -= min_flow;
                    gGraph[ve][vs] += min_flow;
                }
                //回找到 min_flow的层次最小的节点位置
                while (!dQ.empty() && dQ.back() != min_vs){
                    gVisited[dQ.back()] = false;
                    dQ.pop_back();
                }
            }
            else{
                int i = 0;
                for (i = 0; i < n; i++){
                    if (!gVisited[i] && gGraph[v][i] > 0){
                        gVisited[i] = true;
                        dQ.push_back(i);
                        break; //找到一条可以继续走的路,就继续往下走。因为使用栈,所以break,
                        
                    }
                }
                if (i == n) //如果在这一层找不到往下走的路,进行回溯
                    dQ.pop_back();
            }
        }
    }
    return max_flow;
}

 

4. ISAP算法

 
6. 重标号,是将点u重新分层,重新设置点u经过不为0的边可达汇点的最短距离。具体是 dist[u] = min{dist[v]|u连接到v,且u-->v边流量不为0} + 1. 若从u出发的边流量均为0,则无法找到下一个点,则直接将dist[u]置为n(n为节点个数),这样就说明u点不可达。

 

ISAP算法的实现(c++)

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define INFINITE 1 << 28
#define MAX_NODE 205
#define MAX_EDGE_NUM 500
#define min(a,b) a < b?a:b
struct Edge{
	int from;	//起点
	int to;	//终点
	int w;
	int next; //从from 出发的,下一条边的序号
	int rev; //该边的反向边 序号

	//用于查找反向边
	bool operator == (const pair<int,int>& p){
		return p.first == from && p.second == to;
	}
};

Edge gEdges[MAX_EDGE_NUM];

int gHead[MAX_NODE];
int gPre[MAX_NODE];
int gPath[MAX_NODE];
int gGap[MAX_NODE];
int gDist[MAX_NODE];
int gFlow[MAX_NODE][MAX_NODE];


int gSource, gDestination;
int gEdgeCount;

void InsertEdge(int u, int v, int w){		
	Edge* it = find(gEdges, gEdges + gEdgeCount, pair<int, int>(u, v));
	if (it != gEdges + gEdgeCount){ //如果已经有边 u --> v,则之前肯定已经指定了 u-->v 和 v-->u的反向关系
		it->w += w;
	}
	else{ //添加 u-->v的边和反向边 v --> u
		int e1 = gEdgeCount;
		gEdges[e1].from = u;
		gEdges[e1].to = v;
		gEdges[e1].w = w;
		gEdges[e1].next = gHead[u];
		gHead[u] = e1;

		gEdgeCount++;


		int e2 = gEdgeCount;
		gEdges[e2].from = v;
		gEdges[e2].to = u;
		gEdges[e2].w = 0;
		gEdges[e2].next = gHead[v];
		gHead[v] = e2;

		//指定各个边的反向边
		gEdges[e1].rev = e2;
		gEdges[e2].rev = e1;

		gEdgeCount++;
	}	
	gFlow[u][v] = w;
}

//使用bfs进行分层,标记每个点到终点的距离
void Bfs(){
	memset(gGap, 0, sizeof(gGap));
	memset(gDist, -1, sizeof(gDist));

	queue<int> Q;
	gGap[0] = 1;
	gDist[gDestination] = 0;
	Q.push(gDestination);
	while (!Q.empty()){
		int n = Q.front();
		Q.pop();
		for (int e = gHead[n]; e != -1; e = gEdges[e].next){
			int v = gEdges[e].to;
			if (gDist[v] >= 0){ //gDist初始值为-1. 如果>= 0,说明之前已经被访问过了
				continue;
			}
			gDist[v] = gDist[n] + 1;
			gGap[gDist[v]] ++;
			Q.push(v);
		}
	}
}

int ISPA(int n){ //n 为顶点的个数
	int ans = 0, u = gSource, d, e;
	while (gDist[gSource] <= n){
		if (u == gDestination){//进行增广
			int min_flow = INFINITE;
			for (e = gPath[u]; u != gSource; e = gPath[u = gPre[u]]) //找到路径中最小的边流量
				min_flow = min(min_flow, gEdges[e].w);

			for (e = gPath[u = gDestination]; u != gSource; e = gPath[u = gPre[u]]){ //增广操作
				gEdges[e].w -= min_flow;
				gEdges[gEdges[e].rev].w += min_flow;
				gFlow[gPre[u]][u] += min_flow;
				gFlow[u][gPre[u]] -= min_flow;
			}
			ans += min_flow;
		}

		for (e = gHead[u]; e != -1; e = gEdges[e].next){
			if (gEdges[e].w > 0 && gDist[u] == gDist[gEdges[e].to] + 1)
				break;
		}
		if (e >= 0){	//可以向前找到一点,继续扩展
			gPre[gEdges[e].to] = u;
			gPath[gEdges[e].to] = e;
			u = gEdges[e].to;
		}
		else{
			if (--gGap[gDist[u]] == 0){ //gap 优化
				break;
			}
			for (d = n, e = gHead[u]; e != -1; e = gEdges[e].next){ //重标号
				if (gEdges[e].w > 0)
					d = min(d, gDist[gEdges[e].to]); 
			}

			gDist[u] = d + 1;
			++gGap[gDist[u]];
			if (u != gSource) //回溯
				u = gPre[u];
		
		}
	}
	return ans;
}

int main(){
	int u, v, w;
	int n, m;

	while (scanf("%d %d", &m, &n) != EOF){
		gEdgeCount = 0;
		memset(gHead, -1, sizeof(gHead));
		for (int i = 0; i < m; i++){
			scanf("%d %d %d", &u, &v, &w);
			InsertEdge(u, v, w);
		}
		gSource = 1;
		gDestination = n;

		Bfs();
		int result = ISPA(n);
		printf("%d\n", result);
	}
	return 0;
}

 

相关文章: