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;
}

 

posted @ 2012-08-05 00:40  gabo  阅读(292)  评论(0编辑  收藏  举报