• 网络流预流推进(转帖)


    先简单看一下主过程:

    int MAXFLOW()
    {
        hights();
        prepare();
        while (!Q.empty()) {
    	u = Q.get;
    	for each e in G ' (from u) push(e);
    		if (!fixed(u)) reCalc(u);
    	}
    }
    

    接下来介绍算法

    预流推进算法给每一个顶点一个标号h(v),表示该点到t的最短路(在残量网络中)。
    第一步hights()过程,就是BFS出初始最短路,计算出每一个顶点的h(v)。

    预流推进算法的特征是运用了预流来加快运算。预流说明图中的节点(除s, t),仅需要满足流入量 >= 流出量。其中流入量>流出量的接点,我们称之为活动节点。我们的算法就是不断地将活动结点,变为非活动结点,使得预流成为可行流。

    算法过程prepare(),即首先将与s相连的边设为满流,并将这时产生的活动结点加入队列Q。这是算法的开始。
    以后便重复以下过程直到Q为空:
    (1).选出Q的一个活动顶点u。并依次判断残量网络G'中每条边(u, v),若h(u) = h(v) + 1,则顺着这里条边推流,直到Q变成非活动结点(不存在多余流量)。(Push推流过程)
    (2).如果u还是活动结点。则需要对u进行重新标号:h(u) = min{h(v) + 1},其中边(u,v)存在于G' 中。然后再将u加入队列。(reCalc过程)

    可以证明,通过以上算法得到的结果就是最大流。
    如果该算法的Q是标准的FIFO队列,则时间复杂度为(n2m),如果是优先队列,并且标号最高的点优先的话:我们就得到了最高标号预流推进算法,其时间复杂度仅为(n2sqrt(m)),算是比较快的最大流算法了。

    根据上面的伪代码, 实现算法如下

    const int size = 501;
    const int MAX = 1 << 15;
    
    int graph[size][size];
    int label[size];		//标号
    bool visited[size];
    
    bool bfs(int st, int ed)
    {
        memset(label, -1, sizeof(label));
        memset(visited, false, sizeof(visited));
        label[st] = 0;
        visited[st] = true;
        vector < int >plist;
        plist.push_back(st);
        while (plist.size()) {
    	int p = plist[0];
    	plist.erase(plist.begin());
    	for (int i = 0; i < size; i++) {
    	    if (graph[i][p] > 0 && !visited[i]) {
    		plist.push_back(i);
    		visited[i] = true;
    		label[i] = label[p] + 1;
    	    }
    	}
        }
        if (label[ed] == -1) {
    	return false;
        }
        return true;
    }
    
    int inflow[size];		//流入量
    
    int maxFlow()
    {
        memset(inflow, 0, sizeof(inflow));
    
        //hights
        bfs(size - 1, 0);		//end point: size - 1, start point: 0
        memset(visited, false, sizeof(visited));
    
    //prepare()
        vector < int >plist;
        for (int i = 0; i < size; i++) {
    	if (graph[start][i] > 0) {
    	    inflow[i] = graph[start][i];
    	    graph[start][i] -= inflow[i];
    	    graph[i][start] += inflow[i];
    	    if (!visited[i]) {
    		plist.push_back(i);
    		visited[i] = true;
    	    }
    	}
        }
        while (plist.size()) {
    	int p = plist[0];
    	plist.erase(plist.begin());
    	visited[p] = false;
    	int minLabel = -1;
    	for (int i = 0; i < size; i++) {
    	    if (graph[p][i] > 0) {
    		if (label[p] == label[i] + 1) {
    		    int flow = min(inflow[p], graph[p][i]);
    		    inflow[p] -= flow;
    		    inflow[i] += flow;
    		    graph[p][i] -= flow;
    		    graph[i][p] += flow;
    
    		    if (!visited[i] && inflow[i] > 0) {
    			plist.push_back(i);
    			visited[i] = true;
    		    }
    		}
    	    }
    	}
    	if (inflow[p] > 0 && p != end) {
    	    for (int i = 0; i < size; i++) {
    		if (graph[p][i] > 0) {
    		    if (minLabel == -1 || minLabel > label[i] + 1) {
    			minLabel = label[i] + 1;
    		    }
    		}
    	    }
    	    if (!visited[p] && minLabel != -1 && minLabel < size)	//minLabel < size, 这个条件需要加上, 因为经过测试发现有死循环的可能
    	    {
    		for (int i = 0; i < size; i++) {
    		    if (label[i] + 1 == minLabel && graph[p][i] > 0) {
    			visited[p] = true;
    			label[p] = minLabel;
    			plist.push_back(p);
    			break;
    		    }
    		}
    	    }
    	}
        }
        return inflow[end];
    }
    

  • 相关阅读:
    Java学习-IO流-read()和write()详解
    JAVA中String类常用构造方法
    java的System.exit(0)和System.exit(1)区别。
    Eclipse快捷键大全
    Java Arraylist的遍历
    Java Map的遍历
    C++求最大公约数,最小公倍数
    C++sort使用实例
    [Project Euler] 题目汇总
    [leetcode]做过的题的目录
  • 原文地址:https://www.cnblogs.com/Open_Source/p/1904898.html
Copyright © 2020-2023  润新知