最大流ISAP算法模版

唉,说多了都是泪,贴一下ISAP的代码就让初学者参考一下,有问题请指出。

#include<iostream>
#include<vector>
using std::vector;
#include<deque>
#include<stack>
using std::stack;
using std::deque;
#include<algorithm>
using std::cin;
template <typename T>
class ISAP {
public:
	ISAP() {
	};
	template<typename U>
	struct Edge
	{
		Edge(int f, int t, U c, U fl) :from(f), to(t), cap(c), flow(fl) {};
		Edge() {};
		int from, to;
		U cap, flow;
	};

	int N;      //顶点的总数
	int E;      //边的总数
	int s;     //指定源s
	int t;      //指定目的t
	int cE;     //目前边数
	vector<Edge<T>> Ed;   //存放边
	vector<vector<int>> G;//图中一个顶点关联的边的标号
	vector<int>         d;//距离标号
	vector<bool>        vis;//表明是否被访问过
	vector<int>         pre;//父亲结点
	vector<int>         gap;//gap优化
	void Init()
	{
		cin >> N >> E;
		Ed.resize(E);
		G.resize(N + 1);
		d.resize(N + 1);
		pre.resize(N + 1);
		gap.resize(N + 1);
		for (int i = 0; i < N + 1; i++)
			d[i] = 0;
		vis.resize(N + 1);
		for (int i = 0; i < N + 1; i++)
			vis[i] = false;
		for (int i = 0; i < N + 1; i++)
			gap[i] = 0;
		cE = 0;
		s = 1;
		t = N;
		for (int i = 0; i < E; i++)
		{
			int u, v;
			T c;
			cin >> u >> v >> c;
			addedge(u, v, c);
		}
	}

	//生成初始距离标号
	void make_d()
	{
		vis[t] = true;                               //标记终点为访问过
		deque<int> dq;                               //队列,广度优先搜索找标记所有到终点距离
		dq.push_back(t);                             //压入终点
		d[t] = 0;                                    //终点到终点距离为0
		while (!dq.empty())                          //队列为空,退出循环,所有点都被标记了
		{
			int front = dq.front(); dq.pop_front();  //获得队首,并弹出
			int Gsize = G[front].size();             //该结点邻居的数量
			for (int i = 0; i < Gsize; i++)          //遍历该结点有所的邻居
			{
				int Enum = G[front][i];              //找出结点和邻居关联的边的编号
				if (front == Ed[Enum].to)            //如果结点是这条边的终点
				{
					int aim = Ed[Enum].from;         //获得边的起点

					if (Ed[Enum].cap - Ed[Enum].flow>0 && !vis[aim])//邻居有残余流量流向结点,并且邻居没有被访问过
					{
						d[aim] = d[front] + 1;    //标记邻居的距离为结点距离+1
						gap[d[aim]]++;                //有d[aim]距离的结点个数+1
						vis[aim] = true;              //标记邻居为访问过
						dq.push_back(aim);            //将邻居压入队列
					}
				}
				else if (front == Ed[Enum].from)    //结点为边的起点
				{                      
					int aim = Ed[Enum].to;             //获得邻居 
					if (Ed[Enum].flow > 0 && !vis[aim])//邻居有残余流量流向起点,并且邻居没有被访问过
					{
						d[aim] = d[front] + 1;         //更新邻居的距离
						gap[d[aim]]++;         //有d[aim]距离的结点个数+1 
						vis[aim] = true;               //标记邻居为访问过
						dq.push_back(aim);             //将邻居压入队列
					}
				}
			}
		}
		for (int i = 0; i < N + 1; i++)
			vis[i] = false;
	}

	void search()
	{
		pre[s] = -1;                                 //特殊处理,起点在增广路径中没有前置边,置为-1
		bool flag = true;                            //是否退出循环的标志
		while (flag)
		{
			//找增广路
			int cur = s;                             //当前结点为起始结点
			T minFlow = 10000000;          //流无穷大
			while (cur != t)                         //当前结点未到达终止结点
			{
				int Gsize = G[cur].size();           //当前结点邻居的数量
				bool find = false;         //标记没有找到可行孤
				int Minaimdis = N - 1;              //在找不到可行孤的情况下,标记最小距离的孤
				for (int i = 0; i < Gsize; i++)     //遍历没有邻居
				{
					Edge<T> &e = Ed[G[cur][i]];     //当前邻居关联的边
					if (e.to == cur && e.flow > 0)  //当前结点是终止结点,并且有残余流量流向邻居结点
					{
						int from = e.from;          //邻居结点
						if (d[from] == d[cur] - 1)  //如果满足邻居结点的距离 + 1 = 当前结点的距离
						{
							find = true;            //找到可行孤
							pre[from] = G[cur][i];  //标记邻居结点的前置边
							cur = from;             //当前结点更新为邻居结点
							minFlow = std::min(minFlow, e.flow); //更新增广路的流量
							break;                  //不用再从邻居中找了
						}
						Minaimdis = std::min(Minaimdis, d[from]); //邻居没有找到,更新最小距离的孤
					}
					else if (e.from == cur && e.cap - e.flow > 0) //同上
					{
						int to = e.to;
						if (d[to] == d[cur] - 1)
						{
							find = true;
							pre[to] = G[cur][i];
							cur = to;
							minFlow = std::min(minFlow, e.cap - e.flow);
							break;
						}
						Minaimdis = std::min(Minaimdis, d[to]);
					}
				}
				if (!find)                               //未找到可行孤
				{
					gap[d[cur]]--;                                           //将当前结点距离数量-1
					if (gap[d[cur]] == 0) { flag = false; break; }           //如果该距离数量变为0,则出现断层,已经找到找大流

					d[cur] = Minaimdis + 1;        //更新最小距离,最在为N
					gap[d[cur]]++;                         //更新gap
					int edgenum = pre[cur];        //找到当前结点关联的前置边
					if (edgenum == -1)cur = s;             //表示没有前置边,为起点,标记当前结点为起点
					else if (cur == Ed[edgenum].to)        //如果当前结点为前置边的终点
						cur = Ed[edgenum].from;      //标记当前结点为前置边的起点
					else
						cur = Ed[edgenum].to;
				}
			}

			if (cur == t)//找到增广路,调整流量
			{
				while (cur != s)
				{
					Edge<T>& e = Ed[pre[cur]];
					if (cur == e.to)
					{
						e.flow += minFlow;
						cur = e.from;
					}
					else
					{
						e.flow -= minFlow;
						cur = e.to;
					}
				}
			}
		}
	}

	void output()
	{
		T ans = 0;
		for (int i = 0; i < G[s].size(); i++)
		{
			ans += Ed[G[s][i]].flow;
		}
		using std::cout;
		cout << ans;
	}

	void addedge(int from, int to, T cap)
	{
		G[from].push_back(cE);
		G[to].push_back(cE);
		Ed[cE++] = Edge<T>(from, to, cap, 0);
	}
};

int main()
{
	ISAP<int> isap;
	isap.Init();
	isap.make_d();
	isap.search();
	isap.output();
	return 0;
}

  

posted @ 2017-04-24 09:09  风轻云淡走天涯  阅读(202)  评论(0编辑  收藏  举报