参考博文
只列出几种常见的,快的网络流算法
1.Dinic
struct Edge{
int v, w, nxt;
};
Edge edge[maxn << 1];
int pre[maxn], tot, dis[maxn << 1], cur[maxn];
void init() {
tot = 0;
memset(edge, 0, sizeof(edge));
memset(pre, -1, sizeof(pre));
}
void addEdge(int s, int e, int w) {
edge[tot].v = e;
edge[tot].w = w;
edge[tot].nxt = pre[s];
pre[s] = tot ++;
}
int n, m, s, e;
int bfs() {
memset(dis, -1, sizeof(dis));
dis[e] = 0;
queue<int> que;
que.push(e);
while(!que.empty()) {
int u = que.front(); que.pop();
for (int i = pre[u]; i + 1; i = edge[i].nxt) {
if(dis[edge[i].v] == -1 && edge[i ^ 1].w > 0) {
dis[edge[i].v] = dis[u] + 1;
que.push(edge[i].v);
}
}
}
return dis[s] != -1;
}
int dfs(int u, int flow) {
if(u == e) return flow;
int belta = flow;//belta 可以看作是从源点到此处的最大容量
for (int &i = cur[u]; i + 1; i = edge[i].nxt) {
//cur[i] = i;
if(dis[u] == dis[edge[i].v] + 1 && edge[i].w > 0) {
int d = dfs(edge[i].v, min(belta, edge[i].w));
edge[i].w -= d; edge[i ^ 1].w += d;
belta -= d;
if(belta == 0) break;//如果容量为0,那么这条路就不再有增广路,break
}
}
return flow - belta;//返回的是 最大容量-剩余容量
}
int dinic() {
int ans = 0;
while(bfs()) {
for (int i = 1; i <= n; i ++)
cur[i] = pre[i];
ans += dfs(1, INF);
}
return ans;
}
int main(int argc, const char * argv[]) {
int T, karse = 0;
scanf("%d", &T);
while(T --) {
init();
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i ++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
addEdge(u, v, w);
addEdge(v, u, 0);
}
s = 1; e = n;
printf("Case %d: %d\n", ++karse, dinic());
}
其实SAP与Dinic本质都是一样的,都是对图进行分层,不过Dinic是每跑一次都对dis[]重新编号,这个在数据少的情况下很好用,但数据多的话就会有超时的危险,而SAP是只进行一次BFS,其余情况用gap数组来记录距离汇点的距离,如果某一个距离为0,那么这个图就不再有增广路。
如图:
初始的分层是这样的,1是源点,6是汇点
经过一次查找后
而此时2与3联通,但是不满足dis[2] == dis[3] + 1。这时
而此时1又与2的dis相同,所以1也+1
其余的与这个类似
2.SAP
SAP主函数
//si为源点,ei为汇点
void SAP(int s){
int dx, augc, minDis;
if(s == ei){
isFind = true;
maxFlow += aug;
return;
}
augc = aug;
minDis = n - 1;//n个点最大有n-1层
for(int i = pre[s]; i + 1; i = edge[i].nxt){
if(edge[i].w > 0){
if(dis[s] == dis[edge[i].v] + 1){//可行边
aug = min(aug, edge[i].w);
SAP(edge[i].v);//向下 找
if(dis[si] >= n) return;//如果起点的dis>=n,说明有断层直接退出
if(isFind){
dx = i;
break;
}
aug = augc;
}
minDis = min(minDis, dis[edge[i].v]);//找到层数最小的
}
}
if(!isFind){//如果没找到
gap[dis[s]]--;//距离为dis[s]的数量--
if(gap[dis[s]] == 0) dis[si] = n;//有距离汇点dis[s]的点的数量为0,那么已经没有增广路了
dis[s] = minDis + 1;//对于当前的s,距离为minDis+1
gap[dis[s]]++;//gap[dis[s]] ++
}else{//如果找到了,那就
edge[dx].w -= aug;
edge[dx ^ 1].w += aug;
}
}
bfs函数
void bfs() {
memset(dis, -1, sizeof(dis));
memset(gap, 0, sizeof(gap));
gap[0] = 1;
dis[ei] = 0;
que.push(ei);
while(!que.empty()) {
int u = que.front(); que.pop();
for (int i = pre[u]; i + 1; i = edge[i].nxt) {
if(dis[edge[i].v] == -1 && edge[i ^ 1].w) {
dis[edge[i].v] = dis[u] + 1;
que.push(edge[i].v);
gap[dis[edge[i].v]] ++;
}
}
}
}
main函数
bfs();
while(dis[si] < n) {
isFind = 0;
aug = INF;
SAP(si);
}