算法--最大流

最大流

性质

算法输入:
图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此时,已经最优。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章