最大流
性質
算法輸入:
圖G
源點s
匯點e
輸入限制:
G中若存在邊edge,則G中不存在邊edge.Reverse
若edge爲G中的,則有edge.m_nWeight > 0
算法目標:
計算從源點到匯點的最大流
概念:
1.最大流首先是一個可行流
2.每個可行流具備流量屬性,最大流是所有可行流中流量最大的可行流
3.對於滿足輸入限制的G,我們定義G中從s到e的一條可行流爲
從s發出一系列邊,這些邊包含流量屬性。
這些從s發出的流量,在G的邊上流動,最終通過一系列邊,流入e
準確地說即:
可行流stream是G中一系列具有流量屬性的邊的集合
滿足:
若edge爲stream中一條邊,則0 < edge.m_nStream <= edge.m_nWeight
對於G中除s,e的其他節點p,滿足 節點p流入的總流 = 節點p流出的總流
4.我們定義可行流stream的流量爲
從s流出的總流
接口設計
template<typename Key, typename Value>
class MaxStream
{
public:
typename typedef DataStruct::GraphStruct::Graph<Key, Value> InnerGraph;
MaxStream(const InnerGraph& nGraph_);
~MaxStream();
typename InnerGraph Run(const Key& nSourceKey_, const Key& nDestKey_);
private:
void GetAdjustGraph(const InnerGraph& nStreamGraph_,
const InnerGraph& nGraph_,
InnerGraph& nAdjustGraph_);
private:
MaxStream(const MaxStream&) = default;
MaxStream& operator=(const MaxStream&) = default;
private:
const InnerGraph& m_nGraph;
};
實現
構造
template<typename Key, typename Value>
MaxStream<Key, Value>::MaxStream(const InnerGraph& nGraph_)
: m_nGraph(nGraph_)
{
}
析構
template<typename Key, typename Value>
MaxStream<Key, Value>::~MaxStream()
{
}
算法實現
template<typename Key, typename Value>
typename DataStruct::GraphStruct::Graph<Key, Value> MaxStream<Key, Value>::Run(const Key& nSourceKey_, const Key& nDestKey_)
{
// 流圖
InnerGraph _nStreamGraph = m_nGraph;
DataStruct::Array::DynArray<typename InnerGraph::Edge*> _arrEdges = _nStreamGraph.GetEdgesArray();
for (int _i = 0; _i < _arrEdges.GetSize(); _i++)
{
_arrEdges[_i]->m_nWeight = 0.0;
}
while (true)
{
// 構造流圖相匹配的調節圖
DataStruct::Array::DynArray<typename InnerGraph::Node*> _arrNodes = _nStreamGraph.GetNodesArray();
InnerGraph _nAdjustGraph;
for (int _i = 0; _i < _arrNodes.GetSize(); _i++)
{
_nAdjustGraph.AddNode(_arrNodes[_i]->GetPair());
}
// 基於流圖,原始圖得到調節圖
GetAdjustGraph(_nStreamGraph, m_nGraph, _nAdjustGraph);
BreadthFirstVisit<Key, Value> _alBreathFirst(_nAdjustGraph);
DataStruct::Array::DynArray<typename BreadthFirstVisit<Key, Value>::Node*> _arrBreadthNodes = _alBreathFirst.Run(nSourceKey_);
BreadthFirstVisit<Key, Value>::Node* _pDest = nullptr;
for (int _i = 0; _arrBreadthNodes.GetSize(); _i++)
{
if (_arrBreadthNodes[_i]->GetGraphNode() == nullptr)
{
throw "graph node not exist";
}
if (_arrBreadthNodes[_i]->GetGraphNode()->GetPair().m_nKey == nDestKey_)
{
_pDest = _arrBreadthNodes[_i];
break;
}
}
if (_pDest->GetPreNode() == nullptr)
{
break;
}
DataStruct::Array::DynArray<typename InnerGraph::EdgeIdentity> _arrEdgesOnPath;
typename InnerGraph::EdgeIdentity _nEdgeIdentity;
if (_pDest->GetPreNode()->GetGraphNode() == nullptr)
{
throw "pre graph node not exist";
}
_nEdgeIdentity.m_nStartKey = _pDest->GetPreNode()->GetGraphNode()->GetPair().m_nKey;
if (_pDest->GetGraphNode() == nullptr)
{
throw "graph node not exist";
}
_nEdgeIdentity.m_nEndKey = _pDest->GetGraphNode()->GetPair().m_nKey;
typename InnerGraph::Edge* _pEdge = _nAdjustGraph.SearchEdge(_nEdgeIdentity);
if (_pEdge == nullptr)
{
throw "edge not exist";
}
double _nMinWeight = _pEdge->m_nWeight;
while (_pDest->GetPreNode() != nullptr)
{
if (_pDest->GetPreNode()->GetGraphNode() == nullptr)
{
throw "pre graph node not exist";
}
_nEdgeIdentity.m_nStartKey = _pDest->GetPreNode()->GetGraphNode()->GetPair().m_nKey;
if (_pDest->GetGraphNode() == nullptr)
{
throw "graph node not exist";
}
_nEdgeIdentity.m_nEndKey = _pDest->GetGraphNode()->GetPair().m_nKey;
_arrEdgesOnPath.Add(_nEdgeIdentity);
_pEdge = _nAdjustGraph.SearchEdge(_nEdgeIdentity);
if (_pEdge == nullptr)
{
throw "edge not exist";
}
if (_pEdge->m_nWeight < _nMinWeight)
{
_nMinWeight = _pEdge->m_nWeight;
}
_pDest = _pDest->GetPreNode();
}
for (int _i = 0; _i < _arrEdgesOnPath.GetSize(); _i++)
{
_nEdgeIdentity = _arrEdgesOnPath[_i];
_pEdge = _nStreamGraph.SearchEdge(_nEdgeIdentity);
if (_pEdge != nullptr)
{
_pEdge->m_nWeight += _nMinWeight;
}
else
{
_pEdge = _nStreamGraph.SearchEdge(_nEdgeIdentity.Reverse());
if (_pEdge != nullptr)
{
_pEdge->m_nWeight -= _nMinWeight;
}
else
{
throw "unexpected exception";
}
}
}
}
return _nStreamGraph;
}
template<typename Key, typename Value>
void MaxStream<Key, Value>::GetAdjustGraph(
const InnerGraph& nStreamGraph_,
const InnerGraph& nGraph_,
InnerGraph& nAdjustGraph_)
{
DataStruct::Array::DynArray<typename InnerGraph::Edge*> _arrEdges = nStreamGraph_.GetEdgesArray();
for (int _i = 0; _i < _arrEdges.GetSize(); _i++)
{
InnerGraph::EdgeIdentity _nIdentity = _arrEdges[_i]->GetIdentity();
double _nStream = _arrEdges[_i]->m_nWeight;
InnerGraph::Edge* _pOriginEdge = nGraph_.SearchEdge(_nIdentity);
if (_pOriginEdge == nullptr)
{
throw "edge not exist";
}
double _nWeight = _pOriginEdge->m_nWeight;
if (_nWeight - _nStream < 0.0)
{
throw "unexpected error";
}
if (_nWeight - _nStream > 0.0)
{
nAdjustGraph_.AddEdge(_nIdentity);
InnerGraph::Edge* _pEdge = nAdjustGraph_.SearchEdge(_nIdentity);
if (_pEdge == nullptr)
{
throw "edge not exist";
}
_pEdge->m_nWeight = _nWeight - _nStream;
}
if (_nStream < 0.0)
{
throw "unexpected error";
}
if (_nStream > 0.0)
{
nAdjustGraph_.AddEdge(_nIdentity.Reverse());
InnerGraph::Edge* _pEdge = nAdjustGraph_.SearchEdge(_nIdentity.Reverse());
if (_pEdge == nullptr)
{
throw "edge not exist";
}
_pEdge->m_nWeight = _nStream;
}
}
}
算法目標&正確性證明
算法目標分析
算法輸入:
圖G
源點s
匯點e
輸入限制:
G中若存在邊edge,則G中不存在邊edge.Reverse
若edge爲G中的,則有edge.m_nWeight > 0
算法目標:
計算從源點到匯點的一條最大流
概念:
1.最大流首先是一個可行流
2.每個可行流具備流量屬性,最大流是所有可行流中流量最大的可行流
3.對於滿足輸入限制的G,我們定義G中從s到e的一條可行流爲
從s發出一系列邊,這些邊包含流量屬性。
這些從s發出的流量,在G的邊上流動,最終通過一系列邊,流入e
準確地說即:
可行流stream是G中一系列具有流量屬性的邊的集合
滿足:
若edge爲stream中一條邊,則0 < edge.m_nStream <= edge.m_nWeight
對於G中除s,e的其他節點p,滿足 節點p流入的總流 = 節點p流出的總流
4.我們定義可行流stream的流量爲
從s流出的總流
算法正確性證明
首先我們的算法處理過程爲:
給定G中s到e的某可行流stream
循環迭代:
基於可行流stream,構造調節圖
在調節圖中尋找s到e,節點個數最少的一條無環路徑p
若p存在,基於p對stream進行調節,得到調節後的stream
若p不存在,表示stream已經無法繼續調節。stream即爲G中s到e的一條最大流。
因爲基於迭代實現,故正確性分析中,採用循環不變式
要證明算法可以達到目標需要證明
1.循環不變式:
每次循環開始時,stream均爲可行流。且此可行流相比上一次迭代前的流量更大。
2.當迭代中,無法找到p時,表明此時stream已經是最大流
先證明循環不變式:
初始時,stream是可行流。滿足。
假設第k次迭代時,依據循環不變式,此時stream是一個可行流
首先,stream是G中邊集合的一個子集。每條邊edge有流量stream,且0 < edge.stream <= edge.weight
AdjustG是一個圖。具備和G一致的節點集合。
AdjustG中邊的原則爲:
若,edge爲stream一條邊,
若edge.weight - edge.stream > 0,則edge也爲AdjustG中一條邊。邊的權重爲 edge.weight - edge.stream。
edge.Reverse也爲AdjustG中一條邊。邊的權重爲 edge.stream。
這樣處理後,
意味着,AdjustG中我們允許對
stream某邊edge,繼續增加改變流量,或者減小改變流量來進行調節。
這和實際的流的調節過程一致。
在AdjustG中,
若我們找到一條從s到e的無環的,節點個數最小的路徑p:s->x1->...->xn->p
則,我們基於p對流stream進行調節
具體爲:
先得到路徑p上邊的集合,及改集合中邊的最小權重weight。
對該集合中每條邊edge
若該邊是stream邊集合中的邊,
則,調整edge的流量爲 edge.stream += weight。
其中,
weight <= edge.weight
edge.stream + weight <= edge.weight
若該邊不是stream邊集合中的邊,
若,edge.Reverse必是stream的邊集合中的邊
則,調節stream中邊edge.Reverse的流量爲 edge.Reverse.stream - weight
其中,
weight <= edge.weight
edge.Reverse.stream - weight >= 0
則,基於p對路徑stream進行調節後,
一方面,調節後,stream中每條邊的流量均滿足0 < stream <= 此邊在G的weight
另一方面,路徑p的邊集合,滿足
對除s,e外其他節點,流入 = 流出
對s有,流出爲 weight
將其更新到stream中後
stream邊集合仍然滿足,對於G中除s,e外節點p
滿足p的流入 = p的流出
對s,s的流出在迭代後增加了weight
綜合,通過迭代,我們更新後的stream仍然是一個可行流,且此可行流相比迭代前,流量提升了weight
循環不變式,成立。
2.證明,迭代中,無法找到p是stream已經是最大流
採用反證法
假設此時stream不是最大流
我們設最大流爲stream_max
綜合,
假設成立下,此時在AdjustG中必然存在一個邊的子集。
滿足,此邊的子集中,
對於除s,e外其餘節點p,在此邊的子集下,流入 = 流出
對於s,節點s的流出爲某個正值。
則,此時AdjustG中必然存在從s到e的路徑p
這與此時,對AdjustG運行廣度優先,無法獲取p相互矛盾
故,假設不成立
即,這種情況下stream此時,已經最優。