唉,说多了都是泪,贴一下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;
}