zoukankan      html  css  js  c++  java
  • poj 1273 || hdu 1532 Drainage Ditches 最大流

    //poj 1273 || hdu 1532 Drainage Ditches 最大流

    //普通最大流

    //这里给出dinic的递归和非递归代码

    //非递归:
    //用dfs或bfs寻找一条增广路,并进行分层,比如 i能到 j,
    //则 j的层次比 i多 1。如果聚点有在 增广路上(即有对应的
    //层次,否则说明已经达到最大流了),然后顺着层次寻找一条
    //到达聚点的路,并记录下来,若某个结点流向下一结点时,流
    //用不完,则说明可能有分支,记录最后一个分支,若找到聚点
    //时停止查找。如果路径上最后一个结点为聚点时,计算能到达
    //聚点的流量,并建立反向边。之后从分支点找另一条能到聚点
    //的路。若路径上最后一个结点不是聚点时,跳向分支点前一个
    //结点找路,若一直找不到,直到源点时,继续dfs或bfs找增广路

    //递归:
    //bfs对残余网络分层,若到达不了sink则表示没有增广路了,
    //否则sink也分完层后就开始深搜从前一个节点到下
    //一个节点要有容量且下一个节点的层次要比前一节点多1才可继续深搜

    //非递归:记得保存分支的地方(上条边的流量流了一些到聚点后还有剩时);
    //注意用dinic的话如果用邻接表会tle,用邻接矩阵则不会。
    //dinic时,若深搜到的点不能到达sink的点的层次要标为 -1,
    //表示不能到达sink

    //具体看代码

    递归
    //递归
    #define comein freopen("in.txt", "r", stdin);
    #include <stdio.h>
    #include <string.h>
    #include <queue>
    #include <algorithm>
    using namespace std;
    
    
    #define INF 1<<30
    #define N 500
    
    int eid, n_point;
    int map[N][N];      //用邻接表居然tle了
    int head[N], to[N], cap[N], next[N];
    int level[N];
    
    void add_edge(int from, int t, int f)
    {
        to[eid] = t;
        cap[eid] = f;
        next[eid] = head[from];
        head[from] = eid++;
    
        to[eid] = from;   //建反向边
        cap[eid] = 0;
        next[eid] = head[t];
        head[t] = eid++;
    }
    
    bool bfs(int root)     //残余网络分层
    {
        memset(level, -1, sizeof(level));
        queue<int>que;
        que.push(root);
        level[root] = 0;
        while(!que.empty())
        {
            root = que.front();
            que.pop();
    //        for(int i = head[root]; i != -1; i = next[i])
            for(int i = 1; i <= n_point; ++i)
            {   //边还有残余容量,且下一个点还没分层
                if(map[root][i] > 0 && level[i] == -1)
                {
                    level[i] = level[root] + 1;
                    if(i == n_point)
                        return true;
                    que.push(i);
                }
    //            if(level[to[i]] == -1 && cap[i])
    //            {
    //                level[to[i]] = level[root] + 1;
    //                if(to[i] == n_point)
    //                    return true;
    //                que.push(to[i]);
    //            }
            }
        }
        return false;
    }
    
    int dfs(int root, int f)
    {
        if(root == n_point)
            return f;
        int flow, ans = 0;
    //    for(int i = head[root]; i != -1; i = next[i])
        for(int i = 1; i <= n_point; ++i)
        {
    //        int m = min(cap[i], f - ans);   //记得这里要 f-ans
            int m = min(map[root][i], f-ans);
            if(map[root][i] > 0 && level[root] + 1 == level[i] &&
               ans < f && (flow = dfs(i, m) )   )
            {
                map[root][i] -= flow;
                map[i][root] += flow;
                ans += flow;
            }
    //        if(cap[i] && level[root]+1 == level[to[i]] && ans < f &&
    //           (flow = dfs(to[i], m ) )   )
    //        {   //下标从0开始,则正向边为偶数,反向边为基数;
    //            cap[i] -= flow;
    //            cap[i^1] += flow;//下标从1开始则不能用  异或
    //            ans += flow;
    //        }
        }
        if(ans == 0)    //若没找到增广路,这时注意要把该点
            level[root] = -1;   //的层次标为-1,表示不能到达
        return ans;
    }
    
    void dinic()
    {
        int flow = 0;
        while(bfs(1))  //分层
        {
            while(1)    //每条增广路都有一个瓶颈(增光路上最小容量的边)
            {   //每次分层都要把所有增广路的瓶颈都清除掉,再重新分层
                int f = dfs(1, INF);//因此最多层分v次
                if(f == 0)
                    break;
                flow += f;
            }
        }
        printf("%d\n", flow);
    }
    
    int main()
    {
        comein
        eid = 0;
    
        int n_edge;
        while(scanf("%d%d", &n_edge, &n_point) != EOF)
        {
            memset(map, 0, sizeof(map));
    //        memset(head, -1, sizeof(head));
            while(n_edge--)
            {
                int from, to, f;
                scanf("%d%d%d", &from, &to, &f);
                map[from][to] += f;
    //            add_edge(from, to, f);
            }
            dinic();
        }
        return 0;
    }
    非递归
    //Dinic    非递归
    //邻接矩阵
    #include <stdio.h>
    #include <string.h>
    #define N 205
    
    
    int n_node, n_edge;
    int map[N][N], level[N], que[N*N], path[N];
    
    int bfs(int s)
    {
        memset(level, -1, sizeof(level));
    
        int head = 0, tail = 0;
        que[tail++] = s;
        level[s] = 1;   //源点标记为第一层
    
        while(head < tail)
        {
            int now = que[head++];
            for(int i = 1; i <= n_node; ++i)
            {
                if(map[now][i] != 0 && level[i] == -1)
                {
                    level[i] = level[now] + 1;  //标记层数
                    que[tail++] = i;
                }
            }
        }
        //如果聚点没有被分层的话,说明已经达到最大流了
        return level[n_node] + 1;
    }
    
    int dinic(int s)
    {
        int max_flow = 0;
        while(bfs(s))    //广搜对一条增广路上的点分层
        {
            int  pos = 0, cut;
            path[++pos] = s;    //记录增广路
            while(1)
            {
                int flow = 1 << 30;
                int find = 1;
                //若增广路的最后一个结点是聚点,或者流不能流到下一个结点时,结束
                while(path[pos] != n_node && find)
                {
                    find = 0;
                    for(int i = 1; i <= n_node; ++i)
                    {
                        if(level[i] == level[path[pos]] + 1 && map[path[pos]][i] != 0)
                        {
                            find = 1;
                            path[++pos] = i;
                            break;
                        }
                    }
                }
                if(path[pos] == n_node) //若有找到增广路
                {
                    for(int i = 1; i < pos; ++i)
                    {
                        if(flow > map[path[i]][path[i+1]])
                        {
                            flow = map[path[i]][path[i+1]];
                            cut = i;    //记录分叉的地方,这个地方的流没全用完
                        }
                    }
                    max_flow += flow;
                    for(int i = 1; i < pos; ++i)
                    {
                        map[path[i]][path[i+1]] -= flow;
                        map[path[i+1]][path[i]] += flow;    //建反向边
                    }
                    pos = cut;  //从流没用完的边,也就是分支处继续找下一条到聚点的路
                }
                else    //若没有到达聚点
                {
                    if(pos == 1)
                        break;
                    else
                    {
                        level[path[pos]] = -1;  //若这个点不能到达聚点,则去掉
                        pos--;
                    }
                }
            }
        }
        return max_flow;
    }
    
    int main()
    {
        while(scanf("%d%d", &n_edge, &n_node) != EOF)
        {
            memset(map, 0, sizeof(map));
            for(int i = 0; i < n_edge; ++i)
            {
                int from, to, flow;
                scanf("%d%d%d", &from, &to, &flow);
                map[from][to] += flow;
            }
            printf("%d\n", dinic(1));
        }
        return 0;
    }
    EK算法
    //EK算法比较容易理解
    //用广搜找到一条增广路,记录路径,找出增广路中最小
    //的边为流到汇点的流量,建反向边,一直重复道没增广路为止;
    
    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <queue>
    
    using namespace std;
    
    #define INF (1<<30)
    #define N 205
    
    int map[N][N], fa[N];
    bool vis[N];
    
    bool bfs(int n) //广搜找增广路
    {
        memset(vis, false, sizeof(vis));
        memset(fa, -1, sizeof(fa));
        queue<int>que;
        que.push(1);
        vis[1] = true;
        while(!que.empty())
        {
            int now = que.front();
            que.pop();
            vis[now] = true;
            for(int i = 1; i <= n; ++i)
            {
                if(vis[i] == false && map[now][i] > 0)
                {
                    vis[i] = true;
                    fa[i] = now;    //记录路径
                    if(i == n)
                        return true;
                    que.push(i);
                }
            }
        }
        return false;
    }
    
    
    void EK(int n, int m)   //找增广路
    {
        int ans = 0;
        while(bfs(n))
        {
            int min = INF;
            //找出增广路的瓶颈,即增广路中的最短边
            for(int i = n; i != 1; i = fa[i])
                min = map[fa[i]][i] < min ? map[fa[i]][i] : min;
                
            for(int i = n; i != 1; i = fa[i])
            {
                map[fa[i]][i] -= min;
                map[i][fa[i]] += min;   //增加反向流
            }
            ans += min;
        }
        printf("%d\n", ans);
    }
    
    int main()
    {
        int n, m;
        while(scanf("%d%d", &m, &n) != EOF)
        {
            memset(map, 0, sizeof(map));
            for(int i = 0; i < m; ++i)
            {
                int from, to, f;
                scanf("%d%d%d", &from, &to, &f);
                map[from][to] += f;
            }
            EK(n, m);
        }
        return 0;
    }
  • 相关阅读:
    poj 3590 The shuffle Problem——DP+置换
    poj 3128 Leonardo's Notebook——思路(置换)
    bzoj 1004 [HNOI2008]Cards && poj 2409 Let it Bead ——置换群
    bzoj 1119 [POI2009]SLO && bzoj 1697 [Usaco2007 Feb]Cow Sorting牛排序——思路(置换)
    bzoj 3944 Sum —— 杜教筛
    bzoj 1367 [ Baltic 2004 ] sequence —— 左偏树
    bzoj 2093 [ Poi 2010 ] Frog —— 滑动窗口 + 倍增
    bzoj 2276 [ Poi 2011 ] Temperature —— 单调队列
    bzoj 2069 [ POI 2004 ] ZAW —— 多起点最短路 + 二进制划分
    NOIP2007普及 守望者的逃离
  • 原文地址:https://www.cnblogs.com/gabo/p/2623531.html
Copyright © 2011-2022 走看看