Это 20-й день моего участия в августовском испытании обновлений. Ознакомьтесь с подробностями мероприятия: Испытание августовского обновления
Основные свойства Flow Networks
В теории графов сетевой поток определяется как ориентированный граф, содержащий начальную точку.Sисточник и пункт назначенияTarget и несколько ребер, соединяющих вершины. Каждое ребро имеет свою пропускную способностьCПропускная способность, которая является максимальным трафиком, который может разрешить ребро.
трафик в сетевом потокеСледующие условия должны быть выполнены
- От узлапоток к узлутрафик, не могу сравнитьизcapacityеще большой,
- Если определен подчиненный узелпоток к узлуПоток имеет 5 единиц,, то подчиненный узелпоток к узлуПоток имеет -5 единиц,
- Для узлов, отличных от источника и цели на графике, сумма всего входящего трафика должна быть равна сумме всего исходящего трафика.
- То есть поток не будет увеличиваться или уменьшаться без причины, что можно расценивать как своего рода сохранение энергии.
- Возьмем в качестве примера следующий рисунок: поток в узел A равен 6, поток из узла A также равен 6, и то же верно для C и D.
Максимальный поток:Сеть позволяет из источникаSпоток течет до концаTМаксимальный трафик ARGET
Алгоритм Форда-Фалкерсона (или алгоритм Эдмондса-Карпа, если используется BFS) представлен ниже для решения этой проблемы.
Ford-Fulkerson Algorithm
Алгоритм Форда-Фалкерсона требует двух вспомогательных средств.
- Остаточные сети
- Увеличение путей
Residual Networks
Остаточная сеть представляет собой график оставшегося допустимого трафика для каждого ребра в графе. Следующий график является примером
Если все ребра на пути: S-A-C-D-T имеют поток 6 единиц, то эти ребра,,,,остатокемкостьОба должны быть минус 6. Например,Может удерживать только 3 единицы потока,Может удерживать только 1 единицу потока
какТамединица расхода, тогдаВверхresidual capacityопределяется как:
-
- для исходного края (x, y)емкость
- Указывает, сколько трафика у края (x, y) в настоящее время
- Указывает, сколько трафика может быть размещено на краю (x, y)
Residual Networksтакже является ориентированным графом, где:
- Набор вершин точно такой же, как исходный ориентированный граф.
- Емкость ребраresidual capacityзаменить, как показано ниже
Самое главное, если6 единиц трафика через, то в егоResidual Networks, ребро из вершины C в вершину A будет сгенерировано соответственно, и имеет 6 единицresidual capacity
Какой смысл это делать? Можно использоватьЕсли вы хотите изменить направление движенияпонять
Например, предположив, что начальное состояние теперь восстановлено (нет потоков трафика), теперь через Путь проходят 2 единицы трафика: S-C-A-B-T, но, поскольку нет пути из вершины C в вершину A,следовательно. Предполагая, что из вершины A в вершину C поступает 6 единиц потока (как показано на диаграмме выше), теперь можно начать сВключите 2 единицы потоказабрать, который назначен нана, пока, осталось только 4 единицы расхода, а окончательный результат показан слева на следующем рисунке.Residual Networksкак показано справа
Изображение ниже - еще один пример, найденный в Интернете.
Augmenting Paths
существуетResidual Networks, все доступно из исходниковSнаш подходит к концуTПуть цели, то есть все пути, которые могут увеличить трафик, называется Augmenting Paths (аугментирующие пути)
Возьмите приведенный выше рисунок в качестве примера, есть много возможностей для увеличения путей, таких как
- Путь: S-A-B-T, 1~3 единицы потока
- Потому что на этом пути наименьшая пропускная способность среди всех ребер равна, поэтому допустимый размер потока составляет 1~3
- Путь: S-C-B-D-T, 1~2 единицы потока
- Потому что на этом пути наименьшая пропускная способность среди всех ребер равна, поэтому допустимый размер потока составляет 1~2
Подводить итоги
- видетьОбщий трафик, поступающий в настоящее время в Target, которые должны быть отмечены в левой части рисунка ниже, на краю
flow/capacity
найти на карте- Левая конечная точка притока на рисунке нижеTПоток Аргета составляет 8 единиц.
- найтисколько еще трафика, то есть найтиAugmenting Paths, должен быть вResidual NetworksПосмотрите, как показано справа внизу
- Если она впадает в Путь: S-A-B-T, Путь: S-A-C-D-T, Путь: S-C-B-TНе превышайте минимальную остаточную емкость на путипоток, являются увеличивающими путями
Поговорив о двух вышеупомянутых концепциях, давайте объясним алгоритм алгоритма Форда-Фалкерсона.
- существуетResidual NetworksПоиск поAugmenting Paths
- от
BFS()
Выполните поиск, чтобы убедиться, что увеличивающие пути, найденные каждый раз, должны проходить через наименьшее ребро. - Найти усиливающие пути наМинимальная остаточная емкость, добавьте его вобщий поток
- снова сМинимальная остаточная емкостьвозобновитьResidual Networksна краюresidual capacity
- от
- Повторяйте вышеуказанные шаги до тех пор, пока не останетсяAugmenting Pathsдо того как
Возьмите приведенное выше изображение в качестве примера. Шаги для определения максимального расхода следующие:
- начать с
flow = 0
Инициализировать остаточные сети
- На рисунке с
bfs()
найти изSприбытьTПуть с наименьшим ребром: S-A-B-T-
bfs()
Может быть найдено три кратчайших пути, вот пример S-A-B-T
-
- Наблюдайте за краем пути: S-A-B-T и найдитеимеет самый маленькийresidual capacity , поэтому обновите:Общий поток увеличился на 3
- Обновить остаточную емкость ребра
- в пути, с
bfs()
найти изS*прибытьTПуть с наименьшим ребром: S-C-D-T
-
Наблюдайте за краем пути: S-C-D-T и найдитеимеет самый маленькийresidual capacity , поэтому обновите:Общий поток увеличился на 7
-
Обновить остаточную емкость ребра
Затем повторите вышеуказанные шаги:Обновите остаточные сети, найдите увеличивающиеся пути
- Найти путь: S-C-B-T
- возобновитьResidual Networks
- Найти путь: S-A-C-B-T
- возобновитьResidual Networks
- Найти путь: S-A-C-B-D-T
- возобновитьResidual Networks
- Найдите максимальный поток = 17
Полный код выглядит следующим образом
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
class Graph {
private:
int num_vertex; //顶点
vector<vector<int>> map; // 邻接矩阵
public:
Graph():num_vertex(0){};
Graph(int n);
void AddEdge(int from, int to, int capacity);
void FordFulkerson(int s, int t);
bool BfsFindExistingPath(vector<vector<int>> graph, int* predecessor, int s, int t);
int MinCapacity(vector<vector<int>> graph, int* predecessor, int t);
};
Graph::Graph(int n):num_vertex(n) {
map.resize(num_vertex);
for (int i = 0; i < num_vertex; i++)
map[i].resize(num_vertex);
}
bool Graph::BfsFindExistingPath(vector<vector<int>> graph, int* predecessor, int s, int t) {
int visited[num_vertex];
for (int i = 0; i < num_vertex; i++) {
visited[i] = 0; // 0 表示没有访问过
predecessor[i] = -1;
}
queue<int> queue;
queue.push(s);
visited[s] = 1; // 1 表示访问过
while (!queue.empty()) {
int vertex = queue.front(); queue.pop();
for (int j = 0; j < num_vertex; j++) {
if (graph[vertex][j] != 0 && visited[j] == 0) {
queue.push(j);
visited[j] = 1;
predecessor[j] = vertex;
}
}
}
return (visited[t] == 1); // 若t被访问过,表示有path从s到t
}
int Graph::MinCapacity(vector<vector<int>> graph, int* predecessor, int t) {
int min = 0x3f3f3f; // 确保min会更新,假设graph上的capacity都小于0x3f3f3f
// 用predecessor[idx]和idx表示一条edge
// 找到 从s到t 的path中,capacity最小的值,存入min
for (int idx = t; predecessor[idx] != -1; idx = predecessor[idx])
if (graph[predecessor[idx]][idx] != 0 && graph[predecessor[idx]][idx] < min)
min = graph[predecessor[idx]][idx];
return min;
}
void Graph::FordFulkerson(int s, int t) {
vector<vector<int>> graphResidual(map);
int maxflow = 0;
int predecessor[num_vertex];
// bfs find augmeting path
while (BfsFindExistingPath(graphResidual, predecessor, s, t)) {
int min_capacity = MinCapacity(graphResidual, predecessor, t);
maxflow = maxflow + min_capacity;
for (int y = t; y != s; y= predecessor[y]) {
// update residual graph
int x = predecessor[y];
graphResidual[x][y] -= min_capacity;
graphResidual[y][x] += min_capacity;
}
}
cout << "Maximum Flow: " << maxflow << endl;
}
void Graph::AddEdge(int from, int to, int capacity) {
map[from][to] = capacity;
}
int main() {
Graph g(6);
g.AddEdge(0, 1, 9);g.AddEdge(0, 3, 9);
g.AddEdge(1, 2, 3);g.AddEdge(1, 3, 8);
g.AddEdge(2, 4, 2);g.AddEdge(2, 5, 9);
g.AddEdge(3, 2, 7);g.AddEdge(3, 4, 7);
g.AddEdge(4, 2, 4);g.AddEdge(4, 5, 8);
g.FordFulkerson(0, 5); // 指定source为0,target为5
return 0;
}