/// <summary> /// Поиск максимального потока /// </summary> /// <param name="graph">Граф (сеть)</param> /// <param name="startVertex">Начальная вершина цепи</param> /// <param name="finishVertex">Конечная вершина цепи</param> /// <param name="divisionOfVertices">Разбиение множества вершин на два - для построения минимального разреза</param> /// <param name="egVis">Элемент управления визуализации графа. В случае null визуализация отсутствует</param> /// <returns></returns> public int FindMaximalFlow(EduGraph graph, int startVertex, int finishVertex, out List <EdgeInfo> minimalCut, EduGraphVisualizer egVis = null) { int maximalFlow = 0; // искомая величина максимального потока bool[] divisionOfVertices = new bool[graph.VerticesCount]; // разбиение множества вершин на два подмножества (true/false) - для нахождения разреза int[] augmentalPath; // Пока у нас есть аугментальная цепь while ((augmentalPath = GetAugmentalPath(graph, startVertex, finishVertex, divisionOfVertices)) != null) { int augmentalFlow = GetAugmentalFlow(graph, augmentalPath, egVis); // Визуализация => засыпаем на время if (egVis != null) { Thread.Sleep(750); } StartAugmentalFlow(graph, augmentalPath, augmentalFlow); // Визуализация => Рисуем изменения if (egVis != null) { egVis.DrawGraph(); egVis.Update(); } // Визуализация => засыпаем на время, чистим метки if (egVis != null) { Thread.Sleep(1500); graph.ClearVerticesOutsideLabels(); egVis.ClearEdgesMarking(); egVis.ClearVerticesMarking(); egVis.DrawGraph(); egVis.Update(); Thread.Sleep(1000); } // Пересоздаём массив посещённых вершин к следующему разу. Если вдруг цепь не найдётся - он сохранится divisionOfVertices = new bool[graph.VerticesCount]; } // Суммируем исходящий из истока поток - это и будет максимальным потоком foreach (var vertexEdge in graph.Matrix[startVertex].Values) { maximalFlow += vertexEdge != null ? vertexEdge.Flow : 0; } // Находим минимальный разрез minimalCut = BuildMinimalCut(graph, divisionOfVertices); return(maximalFlow); }
/// <summary> /// Метод нахождения величины дополнительного потока, который можно /// пустить по аугментальной цепи /// </summary> /// <param name="graph">Граф (сеть)</param> /// <param name="augmentalPath">Аугментальный путь в виде последовательности ID вершин</param> /// <param name="egVis">Элемент управления визуализации графа. В случае null визуализация отсутствует</param> /// <returns></returns> public int GetAugmentalFlow(EduGraph graph, int[] augmentalPath, EduGraphVisualizer egVis = null) { int augmentalFlow = int.MaxValue; // искомое значение аугментального потока int augmentalPathLength = augmentalPath.Length; for (int curVertexIndex = 0; curVertexIndex < augmentalPathLength; curVertexIndex++) { // Визуализация => засыпаем на время if (egVis != null) { Thread.Sleep(400); } // ID текущей вершины int curVertexID = augmentalPath[curVertexIndex]; // Визуализация => отмечаем текущую вершину if (egVis != null) { egVis.MarkVertex(graph[curVertexID], Color.Red); egVis.Update(); } // Если вершина первая - просто ставим метку и идём к следующей вершине if (curVertexIndex == 0) { // Визуализация => ставим метку if (egVis != null) { // Засыпаем на время Thread.Sleep(400); graph[curVertexID].OutsideLabel = $"(+{curVertexID};inf)"; egVis.DrawGraph(); egVis.Update(); } continue; } // ID предыдущей вершины int prevVertexID = augmentalPath[curVertexIndex - 1]; // Выясняем, по какому ребру мы прошли - прямому, или обратному bool isForwardEdge; // прямое ли ребро if (graph[prevVertexID, curVertexID] != null) { isForwardEdge = true; } else { isForwardEdge = false; } // Визуализация => отмечаем дугу, по которой пришли if (egVis != null) { if (isForwardEdge) { egVis.MarkEdge(graph[prevVertexID, curVertexID], Color.Red); } else { egVis.MarkEdge(graph[curVertexID, prevVertexID], Color.Red); } egVis.Update(); } // Выясняем, какой поток можно пропустить по пути, которым мы прошли if (isForwardEdge) { augmentalFlow = Math.Min(augmentalFlow, graph[prevVertexID, curVertexID].GetAugmentalFlow()); } else { augmentalFlow = Math.Min(augmentalFlow, graph[curVertexID, prevVertexID].Flow); } // Визуализация => ставим метку на эту вершину if (egVis != null) { // Определяем знак в метке, который ставится перед предыдущей вершиной char sign = isForwardEdge ? '+' : '-'; // Засыпаем на время Thread.Sleep(400); // Ставим на вершину метку graph[curVertexID].OutsideLabel = $"({sign}{prevVertexID};{augmentalFlow})"; egVis.DrawGraph(); egVis.Update(); } } return(augmentalFlow); }