poj1273网络流入门题

poj1273网络流入门题

链接:http://poj.org/problem?id=1273
- 题意
这是一个纯正的,最基础的网络流中最大流问题。
给定 m个点,n条边,其中点1是源点,点m是汇点。接下来n行,给你3个数,边的起点,终点,容量,问你这幅图中最大流的值是多少。两点之间可能存在多条边,所以记两点之间的容量要用+=。

  • 关于网络流最大流
    假设最大流问题就像从水坝出水,流到水池一样,途中经过多个水龙头,问水流从水坝流出,有多少水同时流入水池。水流流入水龙头的水量与流出水龙头的水量一致。
    一般计算这个的时候就是寻找到水池的所有可行路径(边不重复),把每条可行路径的水流加在一起就是最大流。从别人博客拉了幅图,自己计算看看最大流
    这里写图片描述
    这个值是50

  • 基本做法
    Edmonds-Karp ,这个算法是选取一个从源点经过点最少到达汇点的路径,获取这条路径中最小的容量边的容量,这就是这条路径可以提供的最大容量,在图中这条路径每条边都减去这个容量后获得的图继续寻找可以从源点到达汇点的路径,把所有的路径获取的容量加在一起就是这幅图的最大流。
    在减容量的时候我们需要增加一个反向边,原因嘛,咱不会解释,就举个例子(难看不要在意O(∩_∩)O谢谢)。

    • 例子:
      这里写图片描述
      这里每条边的容量都是1,假如你第一次选择的路径是1->2->4->6那么这幅图的最大流量就只能是1了,你想走1->3->4这样就没有路径到达6,而你在选择这条路径后在2,4加一条边4->2,那么接下来就可以有另一条路径1->3->4->2->5->6,这样最大流量就是2了。这就是加了反向边的用途。此处只可意会不可言传。

代码部分

Edmonds-Karp

这个是用广度变量bfs,以下是每一次bfs的操作
s是源点,e是汇点。vis数组标记点是否遍历过,pre数组记录点的前驱。在开始把源点加入队列,同时标记,开始将点放入队列,直到汇点进入队列说明路径找到了,在这条路径中寻找边容量最小值,所有边减去最小值,同时构建反向边。

int getflow(int s, int e)
{
    for (int j = 0; j <= m; j++)
    {
        vis[j] = 0;
        pre[j] = 0;
    }
    pre[s] = 0;
    vis[s] = true;
    queue<int> que;
    int temp;
    que.push(s);
    bool haveresult = false;
    while (!haveresult && !que.empty())
    {
        temp = que.front();
        que.pop();
        for (int i = 1; i <= m; i++)
        {
            if (map[temp][i] && !vis[i])
            {
                pre[i] = temp;
                vis[i] = true;
                if (i == m)
                {
                    haveresult = true;
                    break;
                }
                que.push(i);
            }
        }
    }
    if (!haveresult)
        return 0;
    int minflow = inf;
    temp = m;
    while (pre[temp])
    {
        minflow = getmin(minflow, map[pre[temp]][temp]);
        temp = pre[temp];
    }
    temp = m;
    while (pre[temp])
    {
        map[pre[temp]][temp] -= minflow;
        map[temp][pre[temp]] += minflow;
        temp = pre[temp];
    }
    return minflow;
}

Dinic

这个算法是对Edmonds-Karp算法的优化,由于Edmonds-Karp每搜索出一条路径就要进行一次BFS,Dinic就是对BFS的次数进行优化。
先进行BFS对每个点进行分层,源点的层设置为0,layer的值为1就代表源点需要1条边才能到达这个点,遍历到汇点就停止这次的BFS,这样就获得了源点经过汇点layer等级边数的所有点。结束搜索的条件就是BFS到不了汇点。获得BFS的层级后,开始进行DFS,这里我用双端队列进行DFS,将源点从队列尾插入,将队列尾的元素下一个等级的点放入队列,如果队尾元素是汇点,说明找到路径了,同Edmonds-Karp一样步骤,要记录最小边的起点,回溯的时候退到最小边的起点,因为最小边被减后,最小边的起点就无法继续这条路径。

bool getLayer(int s, int e)
{
    int lay = 0;
    int temp;
    queue<int> que;
    for (int j = 0; j <= m; j++)
    {
        layer[j] = -1;
    }
    que.push(s);
    layer[s] = 0;
    while (!que.empty())
    {
        temp = que.front();
        que.pop();
        for (int i = 1; i <= m; i++)
        {
            if (map[temp][i] && layer[i] == -1)
            {
                layer[i] = layer[temp] + 1;
                if (i == m)
                    return true;
                else
                    que.push(i);
            }
        }
    }
    return false;
}
int Dinic()
{
    int Maxflow = 0;
    int temp;
    int minc;
    int mins;//最小边起点
    deque<int> sta;//dfs使用
    while (getLayer(1, m))
    {
        sta.push_back(1);
        for (int i = 1; i <= m; i++)
        {
            vis[i] = 0;
        }
        vis[1] = true;
        while (!sta.empty())
        {
            temp = sta.back();
            if (temp == m)
            {
                minc = inf;
                for (int i = 1; i < sta.size(); i++)
                {
                    int s = sta[i - 1];
                    int e = sta[i];
                    if (map[s][e])
                    {
                        if (minc > map[s][e])
                        {
                            minc = map[s][e];
                            mins = s;
                        }
                    }
                }
                Maxflow += minc;
                for (int i = 1; i < sta.size(); i++)
                {
                    int s = sta[i - 1];
                    int e = sta[i];
                    map[s][e] -= minc;
                    map[e][s] += minc;
                }
                //退栈
                while (!sta.empty() && sta.back() != mins)
                {
                    vis[sta.back()] = false;
                    sta.pop_back();
                }
            }
            else
            {
                int i;
                for (i = 1; i <= m; i++)
                {
                    if (map[temp][i] && layer[i] == layer[temp] + 1 && !vis[i])
                    {
                        vis[i] = true;
                        sta.push_back(i);
                        break;
                    }
                }
                if (i > m)
                    sta.pop_back();
            }
        }
    }
    return Maxflow;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值