C++によるエドモンズ・カープ(Edmonds-Karp)アルゴリズムの実装:最大流を計算するプログラム
本記事では、C++を用いてエドモンズ・カープ(Edmonds-Karp)アルゴリズムを実装し、グラフの始点(ソース)から終点(シンク)までの最大流(Maximum Flow)を計算する方法を解説します。
アルゴリズムの概要
エドモンズ・カープ法は、フォード・ファルカーソン法を改良したアルゴリズムで、増加経路(Augmenting Path)の探索に幅優先探索(BFS)を採用している点が特徴です。常に最短の増加経路から流量を確定していくため、計算量は O(V・E²) に抑えられます。
Begin
function edmondsKarp() :
流量を 0 に初期化する。
ソースからシンクへの増加経路が存在する間、その経路の流量を加算し続ける。
最終的な合計流量を返す。
End
処理の流れ
- BFSによって、残余容量が正の辺のみを通ってシンクに到達できる最短の増加経路を見つける。
- その経路における最小の残余容量(ボトルネック容量)を、今回追加する流量として決定する。
- 順方向の流量を加算し、逆方向の流量を減算することで、フロー全体を更新する。
- 増加経路が見つからなくなった時点で、累積した流量が最大流となる。
サンプルコード
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<iostream>
using namespace std;
int c[10][10]; // 各辺の容量
int flowPassed[10][10]; // すでに流した流量
vector<int> g[10]; // グラフの隣接リスト
int parList[10]; // 経路復元用の親ノード配列
int currentPathC[10]; // 現在の経路における最小残余容量
int bfs(int sNode, int eNode)// 幅優先探索
{
memset(parList, -1, sizeof(parList));
memset(currentPathC, 0, sizeof(currentPathC));
queue<int> q;// キューの宣言
q.push(sNode);
parList[sNode] = -1;// 始点の親を初期化
currentPathC[sNode] = 999;// 始点の残余容量を十分大きな値で初期化
while(!q.empty())// キューが空でない間繰り返す
{
int currNode = q.front();
q.pop();
for(int i=0; i<g[currNode].size(); i++)
{
int to = g[currNode][i];
if(parList[to] == -1)
{
if(c[currNode][to] - flowPassed[currNode][to] > 0)
{
parList[to] = currNode;
currentPathC[to] = min(currentPathC[currNode],
c[currNode][to] - flowPassed[currNode][to]);
if(to == eNode)
{
return currentPathC[eNode];
}
q.push(to);
}
}
}
}
return 0;
}
int edmondsKarp(int sNode, int eNode)
{
int maxFlow = 0;
while(true)
{
int flow = bfs(sNode, eNode);
if (flow == 0)
{
break;
}
maxFlow += flow;
int currNode = eNode;
while(currNode != sNode)
{
int prevNode = parList[currNode];
flowPassed[prevNode][currNode] += flow;
flowPassed[currNode][prevNode] -= flow;
currNode = prevNode;
}
}
return maxFlow;
}
int main()
{
int nodCount, edCount;
cout<<"enter the number of nodes and edges\n";
cin>>nodCount>>edCount;
int source, sink;
cout<<"enter the source and sink\n";
cin>>source>>sink;
for(int ed = 0; ed < edCount; ed++)
{
cout<<"enter the start and end vertex along with capacity\n";
int from, to, cap;
cin>>from>>to>>cap;
c[from][to] = cap;
g[from].push_back(to);
g[to].push_back(from);
}
int maxFlow = edmondsKarp(source, sink);
cout<<endl<<endl<<"Max Flow is:"<<maxFlow<<endl;
}
出力結果
以下は、6頂点・7辺のグラフに対して、始点「0」、終点「4」を指定して実行した例です。各辺については、始点・終点の頂点番号と容量を順に入力しています。
enter the number of nodes and edges 6 7 enter the source and sink 0 4 enter the start and end vertex along with capacity 0 1 14 enter the start and end vertex along with capacity 2 4 10 enter the start and end vertex along with capacity 6 7 9 enter the start and end vertex along with capacity 5 2 10 enter the start and end vertex along with capacity 1 4 12 enter the start and end vertex along with capacity 2 0 15 enter the start and end vertex along with capacity 5 3 15 Max Flow is:12
この入力例では、始点0から終点4まで流せる最大流量が 12 であると計算されています。エドモンズ・カープ法を使うことで、ネットワークフロー問題を効率よく解くことができます。
-
KadaneのアルゴリズムをC++で実装する方法【最大部分配列和の求め方】
Kadane(カダネ)のアルゴリズムは、整数配列の中から連続する部分配列の合計が最大になる組み合わせを効率よく見つけるための手法です。本記事では、その基本的な考え方と、C++による実装例、実行結果について詳しく解説します。 Kadaneのアルゴリズムとは 負の数を含む整数配列が与えられたとき、合計値が最大となる連続した部分配列を探す問題は「最大部分配列和問題」と呼ばれます。すべての部分配列を総当たりで調べるとO(n²)〜O(n³)の時間がかかりますが、Kadaneのアルゴリズムを使えばたった1回の走査(O(n))で答えを求められます。 基本的な考え方はシンプルで、各要素に対して次のどちらか大き
-
ヴィジュネル暗号をC++で実装する方法|暗号化・復号化プログラムの解説
ヴィジュネル暗号(Vigenère Cipher)は、アルファベットのテキストを暗号化するための多表式換字暗号の一種です。鍵の各文字に応じて異なる換字表が切り替わる仕組みのため、単純なシーザー暗号などと比べて、頻度分析による解読への耐性が高いという特徴があります。 この方式の暗号化と復号化には「ヴィジュネル暗号表」を使用します。これは、AからZまでのアルファベットを1行ずつ順にずらしながら26行に並べた、26×26の表です。 暗号化の流れ 鍵:WELCOME 平文:Thisistutorialspoint まず、与えられた鍵を平文と同じ長さに達するまで繰り返し、処理用の鍵列を作成します。