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)