Dinic算法寻找网络最大流的Java实现

1、代码实现

package Graph;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;

public class dinic {
	//定义一个哈希表,即字典,构造原始的图
    HashMap<String, HashMap<String,Integer>> original_graph;
    HashMap<String, HashMap<String,Integer>> residual_graph;
    //定义层次图,层次图的边只能从起点到11223,以此类推
    HashMap<String, HashMap<String,Integer>> level_graph;
    ArrayList<Integer> residual_flow_value = new ArrayList<Integer>();
    ArrayList<String> residual_flow_path = new ArrayList<String>();
    int augmented_value, maximum_flow;
    boolean is_blocking_flow = false;
    //流量 flow 容量  Capacity
 
	public dinic() {
		// TODO Auto-generated constructor stub
    	original_graph = new HashMap<String, HashMap<String,Integer>>();
    	residual_graph = new HashMap<String, HashMap<String,Integer>>();
    	level_graph = new HashMap<String, HashMap<String,Integer>>();
    	augmented_value = 0;
	}
	
    public void initial_original_graph() {//只能用来查询,不能修改
//    	Integer[] weight_edge = new Integer[]{1,2};
    	original_graph.put("s",new HashMap<String,Integer>());//只出不进
    	//1
//    	original_graph.get("s").put("v1",10);
//    	original_graph.get("s").put("v2",10);
////    	System.out.println(original_graph.get("s"));
//    	original_graph.put("v1",new HashMap<String,Integer>());
//    	original_graph.get("v1").put("v2",2);
//    	original_graph.get("v1").put("v3",4);
//    	original_graph.get("v1").put("v4",8);
//    	original_graph.put("v2",new HashMap<String,Integer>());
//    	original_graph.get("v2").put("v4",9);
//    	original_graph.put("v3",new HashMap<String,Integer>());
//    	original_graph.get("v3").put("t",10);
//    	original_graph.put("v4",new HashMap<String,Integer>());
//    	original_graph.get("v4").put("v3",6);
//    	original_graph.get("v4").put("t",10);
//    	original_graph.put("t",new HashMap<String,Integer>());//只进不出
    	//2
    	original_graph.get("s").put("v1",8);original_graph.get("s").put("v2",12);
    	original_graph.put("v1",new HashMap<String,Integer>());
    	original_graph.get("v1").put("v3",6);original_graph.get("v1").put("v4",10);
    	original_graph.put("v2",new HashMap<String,Integer>());original_graph.get("v2").put("v1",2);
    	original_graph.get("v2").put("v3",10);original_graph.put("v3",new HashMap<String,Integer>());
    	original_graph.get("v3").put("t",8);original_graph.put("v4",new HashMap<String,Integer>());
    	original_graph.get("v4").put("v3",2);original_graph.get("v4").put("t",10);
    	original_graph.put("t",new HashMap<String,Integer>());
//    	System.out.println(original_graph.get("t").isEmpty());
     }
    
    public void initial_residual_graph() { // 初始化构造方法和original_graph一样
    	//1
//    	residual_graph.put("s",new HashMap<String,Integer>());residual_graph.get("s").put("v1",10);
//    	residual_graph.get("s").put("v2",10);residual_graph.put("v1",new HashMap<String,Integer>());
//    	residual_graph.get("v1").put("v2",2);residual_graph.get("v1").put("v3",4);
//    	residual_graph.get("v1").put("v4",8);residual_graph.put("v2",new HashMap<String,Integer>());
//    	residual_graph.get("v2").put("v4",9);residual_graph.put("v3",new HashMap<String,Integer>());
//    	residual_graph.get("v3").put("t",10);residual_graph.put("v4",new HashMap<String,Integer>());
//    	residual_graph.get("v4").put("v3",6);residual_graph.get("v4").put("t",10);
//    	residual_graph.put("t",new HashMap<String,Integer>());
    	//2
    	residual_graph.put("s",new HashMap<String,Integer>());residual_graph.get("s").put("v1",8);
    	residual_graph.get("s").put("v2",12);residual_graph.put("v1",new HashMap<String,Integer>());
    	residual_graph.get("v1").put("v3",6);residual_graph.get("v1").put("v4",10);
    	residual_graph.put("v2",new HashMap<String,Integer>());residual_graph.get("v2").put("v1",2);
    	residual_graph.get("v2").put("v3",10);residual_graph.put("v3",new HashMap<String,Integer>());
    	residual_graph.get("v3").put("t",8);residual_graph.put("v4",new HashMap<String,Integer>());
    	residual_graph.get("v4").put("v3",2);residual_graph.get("v4").put("t",10);
    	residual_graph.put("t",new HashMap<String,Integer>());
    }
    
    //使用bfs
    public void initial_level_graph() {
    	//初始化变量
    	level_graph = new HashMap<String, HashMap<String,Integer>>();
    	Queue<String> queue = new LinkedList<String>();
    	ArrayList<String> traversed_node = new ArrayList<String>();//遍历过的点
    	HashMap<String,Integer> node_level = new HashMap<String,Integer>();
    	node_level.put("s",0);
    	traversed_node.add("s");
    	String pointer;
    	queue.offer("s");
    	while(!queue.isEmpty()) {
    		pointer = queue.poll();
    		Iterator<String> iterator = residual_graph.get(pointer).keySet().iterator();
            while (iterator.hasNext()) {
                String key = iterator.next();//表示当前点pointer有连接点,但是不确定这个点是否能加入层次图
                if(!traversed_node.contains(key)) {
                	if (residual_graph.get(pointer).get(key) > 0 ) { // 表示能通过
                		node_level.put(key,node_level.get(pointer)+1);
                		traversed_node.add(key);// 添加遍历过的节点
                        queue.offer(key); // 这一步会把同一level的点全部加到队列中去 
                	}        	
                }
              //下面这个判断条件可以优化
              if (node_level.get(key) != null && node_level.get(pointer) != null) {
                  if (node_level.get(key) > node_level.get(pointer)) {
                	  int value = residual_graph.get(pointer).get(key); //边的剩余容量
                	  // 只有容量大于0才能通过
                      if (value > 0) {
                    	  if (!level_graph.containsKey(pointer)){ // 没有这个key就要重新初始化
                              level_graph.put(pointer,new HashMap<String,Integer>());
                          }
                    	  level_graph.get(pointer).put(key,value);
                      }         
//                      System.out.println(level_graph);
                  }  
              }
            }
    	}
    }
    //dfs,回溯
    //寻找层次图的阻塞流
    public void find_blocking_flow(String node) {
    	HashMap<String,Integer> tempHashMap = level_graph.get(node);
//    	System.out.println(tempHashMap);
    	if (node == "t") {
    		is_blocking_flow = true;
    		ArrayList<Integer> tempArray = new ArrayList<>(residual_flow_value);
    		Collections.sort(tempArray);
    		augmented_value = tempArray.get(0);
    		for (int id = 0; id < residual_flow_path.size()-1 ;id++) {
    			level_graph.get(residual_flow_path.get(id)).replace(residual_flow_path.get(id+1), 
    					level_graph.get(residual_flow_path.get(id)).get(residual_flow_path.get(id+1))-augmented_value);
    		}
    		for (int id = 0; id < residual_flow_value.size() ;id++) { // 存值数组需要动态更新
    			residual_flow_value.set(id, residual_flow_value.get(id)-augmented_value);
    		}
//    		System.out.println(level_graph);
//    		System.out.println(residual_flow_path); //从起点到终点的所有通路
    	}
    	if (tempHashMap == null)  {
    		// 根据层次图的处理,决定使用tempHashMap.isEmpty()还是tempHashMap == null		
    		return;
    	}
    	Iterator<String> iterator = tempHashMap.keySet().iterator();
    	while (iterator.hasNext()) {
            String key = iterator.next();
            int value = tempHashMap.get(key);
            if (value > 0) {
            	residual_flow_value.add(value);
            	residual_flow_path.add(key);
//            	System.out.println(residual_flow_value);
                find_blocking_flow(key);
//              System.out.println(key);
                residual_flow_value.remove(residual_flow_value.size()-1);//去掉最后一个
                residual_flow_path.remove(residual_flow_path.size()-1);//去掉最后一个    
            }        	
       }
    }
	public void updateResidualGraph() {
		// 在层次图寻找到一条阻塞流之后,更新残留网络的流量
		Iterator<String> iterator_1 = level_graph.keySet().iterator();
    	while (iterator_1.hasNext()) {
    		String key_1 = iterator_1.next();
    		HashMap<String,Integer> tempHashMap = level_graph.get(key_1);
    		Iterator<String> iterator_2 = tempHashMap.keySet().iterator();
    		while(iterator_2.hasNext()) {
    			String key_2 = iterator_2.next();
    			int value = tempHashMap.get(key_2);
    			residual_graph.get(key_1).replace(key_2, value);
    		}
    	}
    	// 给残留图添加反向边
    	int inversed_value = 0;
        Iterator<String> iterator_3 = level_graph.keySet().iterator();
        while(iterator_3.hasNext()) {
        	String key_3 = iterator_3.next();
        	// 数组迭代器不能在迭代过程中改变,所以需要有一个不变的数据结构
        	HashMap<String,Integer> tempHashMap = level_graph.get(key_3);
        	Iterator<String> iterator_4 = tempHashMap.keySet().iterator();
    		while(iterator_4.hasNext()) {
    			String key_4 = iterator_4.next();
    			// original_graph.get(key_3).get(key_4) 和 residual_graph.get(key_3).get(key_4) 一定不是null,
    			// 考虑到原始图没有反向边
    			if (original_graph.get(key_3).get(key_4) != null) {
    				inversed_value = original_graph.get(key_3).get(key_4)- residual_graph.get(key_3).get(key_4);
    			}else {
    				inversed_value = original_graph.get(key_4).get(key_3)- residual_graph.get(key_3).get(key_4);
    			}  			
    			residual_graph.get(key_4).put(key_3,inversed_value);
    		}
    		inversed_value = 0;
        }
     // 在dinic算法中,我一开始认为没有必要构造从汇点到汇点上一层的点、源点下一层到源点的反向边了。但我后来觉得不应该。
     // 切记,更新的是残留图,不是层次图。
	}
	public void dinic_max_flow() {
		// 初始化原始的网络图
		initial_original_graph();
		// 初始化残留网络,等于原始的网络图
		initial_residual_graph();
		System.out.println("原始网络图: "+original_graph);
		int iteration = 0;
		while(true) {
			// 初始化残留网络的层次图
			iteration++;
			System.out.println("---------第"+iteration+"次迭代"+"---------");			
			initial_level_graph();
			System.out.println("层次图: "+level_graph);
			residual_flow_path.clear();
	    	residual_flow_path.add("s");
			// 从源点开始寻找改层次图的阻塞流,多个增广路构成阻塞流
			find_blocking_flow("s");
			System.out.println("增广后的层次图: "+level_graph);
			if (!is_blocking_flow) {
				// 一旦残留网络没办法找到阻塞流了,中止循环
				break;
			}
			is_blocking_flow = false;
			//找到之后,开始更新残留网络,并添加反向边
			updateResidualGraph();
			System.out.println("更新流量后的残留图: "+residual_graph);
		}
		System.out.println("最终的残留图: "+residual_graph);
		System.out.println("---------最大流---------");
		get_max_flow();
	}
	
	public void get_max_flow() {
		int capacity = 0;
		/*计算最大容量不能计算原始图源点的出度,注释掉的是错误的*/
//		HashMap<String,Integer> capacityHashMap = original_graph.get("s");
//		Iterator<String> iterator_1 = capacityHashMap.keySet().iterator();
//		while(iterator_1.hasNext()) {
//			String key_1 = iterator_1.next();
//			capacity = capacity + capacityHashMap.get(key_1);
//		}
		Iterator<String> iterator_1 = original_graph.keySet().iterator();
		while(iterator_1.hasNext()) {
			String key_1 = iterator_1.next();
			HashMap<String,Integer> flowHashMap = original_graph.get(key_1);
			Iterator<String> iterator_2 = flowHashMap.keySet().iterator();
			while(iterator_2.hasNext()) {
				String key_2 = iterator_2.next();
				if (key_2 == "t") {
					capacity = capacity + flowHashMap.get(key_2);
				}
			}
		}
		int residual = 0;
		Iterator<String> iterator_3 = residual_graph.keySet().iterator();
		while(iterator_3.hasNext()) {
			String key_3 = iterator_3.next();
			HashMap<String,Integer> flowHashMap = residual_graph.get(key_3);
			Iterator<String> iterator_4 = flowHashMap.keySet().iterator();
			while(iterator_4.hasNext()) {
				String key_4 = iterator_4.next();
				if (key_4 == "t") {
					residual = residual + flowHashMap.get(key_4);
				}
			}
		}
//		System.out.println(capacity);
		maximum_flow = capacity - residual;
		System.out.println("最大流为: "+maximum_flow);
	}
	public static void main(String args[])  {
		dinic d = new dinic(); 
		d.dinic_max_flow();
	}
    
}

2、算法支撑

2.1 dinic算法

目标: 找到最大流 Maximum Flow ,公式: Flow = Capacity - Residual
1、首先,构建一个Residual graph(残留网络),初始化残留网络为原始的网络图
2、重复进行下面的三步:
 2.1 构建残留图的层次图
 2.2 寻找层次图的阻塞流
 2.3 更新残留网络(包括更新残留量、去掉残留量为0的边、添加反向边)
循环中止条件: 层次图中无法找到阻塞流

2.2 寻找阻塞流的算法

1、首先,构建一个残留网络Residual graph,初始化残留网络为原始的网络图
2、当Augmenting path(增广路)被找到时:
 2.1 在残留图中寻找增广路
 2.2 寻找增广路上最小的容量 x
 2.3 更新残留网络 residual = residual - x
循环中止条件: 层次图中无法找到增广流

3、总结

  在代码中有我进行了详细的注释,主要使用 BFS + DFS 的基础算法架构,同时根据我自己的编程习惯,采用双层字典的数据结构实现Dinic算法。
  如果觉得不错,请点个赞吧,不定期更新;如果有错误,还请批评指正,感谢!

你可能感兴趣的:(算法,java,dinic,网络流问题)