网络流之最大流sap算法

sap算法用于求网络流的最大流

算法框架:

1.给每个点标高度 只有高处的水才能往地处流 一开始的高度都为0

2.在所有的可行弧中不断的寻找增广路 可行弧的定义为 {(i,j) | h[i]=h[j]+1}

3.遍历完当前节点后(流不出去了) 重新标记当前点的高度(保证下次再来的时候有路可走) h[i]=min(h[j])+1;

4.检查是否存在断层 如果出现断层 则图中已不存在增广路 算法可以结束 否则从源点开始继续遍历


下面的代码的题目是 usaco 4.2.1 草地排水

/*
PROG:ditch
LANG:C++
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
#define bug cout<<"bug"<<endl;
const int maxn = 300;
const int inf = 123456789;
int s, t;
int g[maxn][maxn];
int h[maxn], vh[maxn];//h个点的高度 vh各个高度点的数量
int n, m;
int aug(int k, int fn)
{
    if (k == t) return fn;//到大汇点 返回
    int left = fn, delta = 0;
    int minh = n - 1;//注意这里 是n-1
    for (int i = 1; i <= n; i++)
        if (g[k][i] > 0)
        {
            if (h[k] == h[i] + 1)
            {
                delta = aug(i, min(left, g[k][i]));
                g[k][i] -= delta;
                g[i][k] += delta;
                left -= delta;
                if (left == 0 || h[s] == n) return fn - left;//这两个一定要加
            }
            minh = min(minh, h[i]);
        }

    if (left == fn)//出不去就升高自己的高度
    {
        vh[h[k]]--;
        if (vh[h[k]] == 0) h[s] = n;//出现断层了 gap优化
        h[k] = minh + 1;
        vh[h[k]]++;
    }
    return fn - left;
}
int sap()
{
    int ans = 0;
    vh[0] = n;
    while (h[s] < n)
        ans += aug(s, inf);

    return ans;
}
int main()
{
    freopen("ditch.in", "r", stdin);
    freopen("ditch.out", "w", stdout);
    cin >> m >> n;
    int a, b, c;
    for (int i = 1; i <= m; i++)
    {
        cin >> a >> b >> c;
        g[a][b] += c;
    }
    s = 1, t = n;
    cout << sap() << endl;
    fclose(stdin);
    fclose(stdout);
    return 0;
}


这里是用邻接表写的

/*
PROG:ditch
LANG:C++
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
struct node
{
    int d, w;
    node *next, *op;//op为反向边的地址
};
const int maxn = 300;
const int inf = 123456789;
node *adj[maxn];
int h[maxn], vh[maxn];
int n, m;//n个点 m条边
int s, t;//源点和汇点

void edge(int a, int b, int c)
{
    node *p = new node, *q = new node;
    //正向边 和 反向边
    p->d = b;
    p->w = c;
    p->next = adj[a];
    adj[a] = p;

    q->d = a;
    q->w = 0;//这里要注意 反向边流量为0
    q->next = adj[b];
    adj[b] = q;

    p->op = q;
    q->op = p;
}
int aug(int k, int fm)
{
    if(k == t)
        return fm;
    int left = fm, delta = 0;
    int minh = n-1;
    for(node *p = adj[k]; p != NULL; p = p->next)
        if(p->w > 0)
        {
            if(h[k] == h[p->d] + 1)
            {
                delta = aug(p->d, min(left, p->w));
                p->w -= delta;
                p->op->w += delta;
                left -= delta;
                if(left == 0 || h[s] == n)
                    return fm - left;
            }
            minh = min(minh, h[p->d]);
        }

    if(fm == left)
    {
        vh[h[k]]--;
        if(vh[h[k]] == 0)
            h[s] = n;
        h[k] = minh + 1;
        vh[h[k]]++;
    }
    return fm - left;
}
int sap()
{
    int ans = 0;
    memset(h, 0, sizeof(h));
    memset(vh, 0, sizeof(vh));
    vh[0] = n;
    while(h[s] < n)
        ans += aug(s, inf);
    return ans;
}
int main()
{
    freopen("ditch.in", "r", stdin);
    freopen("ditch.out", "w", stdout);
    cin >> m >> n;
    int a, b, c;
    for(int i = 1; i <= m; i++)
    {
        cin >> a >> b >> c;
        edge(a, b, c);
    }
    s = 1, t = n;
    cout << sap() << endl;
    fclose(stdin);
    fclose(stdout);
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值