Алгоритм Java MaxFlow, края, генерирующие проблемы
Я пытаюсь решить проблему максимального потока с графом, имеющим неограниченные весовые ребра, но у каждого узла есть емкость. Я решил проблему с помощью алгоритма Форда Фулкерсона и разделил каждый узел на входной и выходной узлы с емкостью, являющейся взвешенным фронтом между ними. Мой алгоритм прекрасно работает, когда я жестко кодирую по краям (см. Закомментированный блок в коде), но всегда возвращает ноль, когда я пытаюсь построить ребра из текстового файла. Что касается жизни, я не могу понять, почему, я проверил, чтобы убедиться, что все ребра строятся правильно, и просто не могу понять, что не так.
Текстовый файл для чтения графика (1-я строка - ребра, 2-я - емкость узлов) (1 2) (2 3) (2 5) (3 4) (4 5) (3 5) (2 1500) (3 1000) (4 800)
import java.util.*;
import java.io.*;
public class Main {
//Add Node to Graph
public static void make_node(Map<String, List<Edge>> graph, String node_v) {
List<Edge> empty = new ArrayList<>();
if(!graph.containsKey(node_v)) {
graph.put(node_v, empty);
}
}
//Create Edge
public static void make_edge(Map<String, List<Edge>> graph, Map<Edge, Integer> flow_graph, String node_u, String node_v, int weight) {
Edge edge = new Edge(node_u, node_v, weight);
Edge residual_flow = new Edge(node_v, node_u, 0);
edge.setRisedge(residual_flow);
residual_flow.setRisedge(edge);
if (graph.containsKey(node_u)) {
List<Edge> edge_list = graph.get(node_u);
edge_list.add(edge);
graph.put(node_u, edge_list);
}
if (graph.containsKey(node_v)) {
List<Edge> edge_list = graph.get(node_v);
edge_list.add(residual_flow);
graph.put(node_v, edge_list);
}
flow_graph.put(edge, 0);
flow_graph.put(residual_flow, 0);
}
//Find valid path to Sink
public static List<Edge> get_path(Map<String, List<Edge>> graph, Map<Edge, Integer> flow_graph, String source, String sink, List<Edge> path) {
if (source == sink)
return path;
int residual;
List<Edge> result;
for (Edge edge : graph.get(source)) {
residual = edge.getCapacity() - flow_graph.get(edge);
if (residual > 0 && !path.contains(edge) && !path.contains(edge.getRisedge())) {
path.add(edge);
result = get_path(graph, flow_graph, edge.getSink(), sink, path);
if (result != null) {
return result;
}
}
}
return null;
}
//Find Max Flow
public static int find_max_flow(Map<String, List<Edge>> graph, Map<Edge, Integer> flow_graph, String source, String sink) {
List<Edge> path = new ArrayList<>();
path = get_path(graph, flow_graph, source, sink, path);
List<Integer> residuals = new ArrayList<>();
int min_path_flow;
while (path != null) {
for (Edge edge : path) {
residuals.add(edge.getCapacity() - flow_graph.get(edge));
}
min_path_flow = Collections.min(residuals);
for (Edge edge : path) {
flow_graph.put(edge, flow_graph.get(edge) + min_path_flow);
flow_graph.put(edge.getRisedge(), flow_graph.get(edge.getRisedge()) - min_path_flow);
}
List<Edge> empty = new ArrayList<>();
path = get_path(graph, flow_graph, source, sink, empty);
}
int max_flow = 0;
for (Edge edge : graph.get(source)) {
max_flow += flow_graph.get(edge);
}
return max_flow;
}
public static void main(String[] args) throws IOException {
Map<String, List<Edge>> graph = new HashMap<>();
Map<Edge, Integer> flow_graph = new HashMap<>();
Map<String, Integer> capacity_dict = new HashMap<>();
List<String> in_out_nodes = new ArrayList<>();
//Get file name
Scanner scan = new Scanner(System.in);
System.out.println("Enter file name:");
String filename = scan.nextLine();
File file = new File(filename);
BufferedReader reader = new BufferedReader(new FileReader(file));
String graph_text = reader.readLine();
String capacity_text = reader.readLine();
//Parse Capacity
capacity_text = capacity_text.replace(")", "");
capacity_text = capacity_text.replace("(", "");
String[] split_capacity = capacity_text.split("\\s");
//Parse Graph
graph_text = graph_text.replace(")", "");
graph_text = graph_text.replace("(", "");
String[] split_graph = graph_text.split("\\s");
//Parse Capacity
for (int i = 0; i < split_capacity.length; i++){
if(!capacity_dict.containsKey(split_capacity[i])){
capacity_dict.put(split_capacity[i],Integer.valueOf(split_capacity[i+1]));
in_out_nodes.add(split_capacity[i]);
i = i+1;
}
}
//Make nodes
for (String s : split_graph){
make_node(graph, s + "out");
make_node(graph, s + "in");
}
//Make edges
for (int i = 0; i < split_graph.length; i ++){
String u = split_graph[i] + "out";
String ui = split_graph[i] + "in";
String v = split_graph[i + 1] + "in";
if(in_out_nodes.contains(split_graph[i])){
in_out_nodes.remove(split_graph[i]);
make_edge(graph,flow_graph,u,ui, capacity_dict.get(split_graph[i]) );
}
if(capacity_dict.containsKey(split_graph[i])){
make_edge(graph,flow_graph,u,v, capacity_dict.get(split_graph[i]) );
}else{
make_edge(graph,flow_graph,u,v, capacity_dict.get(split_graph[i + 1]) );
}
i += 1;
}
//Code works when i comment out my generated edges and use these
//make_edge(graph,flow_graph, "1out","2in",1500);
//make_edge(graph,flow_graph, "2out","3in",1500);
//make_edge(graph,flow_graph, "2out","5in",1500);
//make_edge(graph,flow_graph, "3out","4in",1000);
//make_edge(graph,flow_graph, "4out","5in",800);
//make_edge(graph,flow_graph, "3out","5in",1000);
//make_edge(graph,flow_graph, "2in","2out",1500);
//make_edge(graph,flow_graph, "3in","3out",1000);
//make_edge(graph,flow_graph, "4in","4out",800);
System.out.print(find_max_flow(graph, flow_graph, "1out", "5in"));
}
}
Edge Class
public class Edge {
public Edge(String source, String sink, int capacity) {
this.source = source;
this.sink = sink;
this.capacity = capacity;
}
private String source;
private String sink;
private int capacity;
private Edge risedge;
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getSink() {
return sink;
}
public void setSink(String sink) {
this.sink = sink;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public Edge getRisedge() {
return risedge;
}
public void setRisedge(Edge risedge) {
this.risedge = risedge;
}
}
1 ответ
Ну, если честно, твой код беспорядок. Я не могу сказать, какова ваша точка неудачи. Так как ваша программа работает для жестко закодированных ребер, с вашим графиком что-то не так. И действительно, я создал несколько выходных данных для сравнения обоих графиков.
Ваши жестко закодированные графики выглядят следующим образом. Край представлен
name_of_source -> name_of_sink (capacity)
1in: []
2in: [2in -> 1out (0), 2in -> 2out (0)]
3in: [3in -> 2out (0), 3in -> 3out (0)]
4in: [4in -> 3out (0), 4in -> 4out (0)]
5in: [5in -> 2out (0), 5in -> 4out (0), 5in -> 3out (0)]
1out: [1out -> 2in (1500)]
2out: [2out -> 2in (1500), 2out -> 3in (1500), 2out -> 5in (1500)]
3out: [3out -> 3in (1000), 3out -> 4in (1000), 3out -> 5in (1000)]
4out: [4out -> 4in (800), 4out -> 5in (800)]
5out: []
Но когда вы строите тот же график без жесткого кодирования, вы получаете:
1in: []
2in: [2in -> 1out (0), 2in -> 2out (1500)]
3in: [3in -> 2out (0), 3in -> 3out (1000)]
4in: [4in -> 3out (0), 4in -> 4out (800)]
5in: [5in -> 2out (0), 5in -> 4out (0), 5in -> 3out (0)]
1out: [1out -> 2in (1500)]
2out: [2out -> 3in (1500), 2out -> 5in (1500), 2out -> 2in (0)]
3out: [3out -> 4in (1000), 3out -> 5in (1000), 3out -> 3in (0)]
4out: [4out -> 5in (800), 4out -> 4in (0)]
5out: []
Чтобы определить строковое представление любого класса, вы должны переопределить метод toString() из Object. Я добавил следующий метод в ваш класс Edge для получения этого вывода.
@Override
public String toString(){
return source + " -> " + sink + " (" + capacity + ")";
}
И следующий код производит вывод вашей графовой переменной:
for ( String k : graph.keySet() )
System.out.println( k + ": " + graph.get(k));
Я уверен, что этого достаточно, чтобы помочь вам решить проблему.