poj 2455二分+dinic求最大流

直接搬人家的博客吧,感觉比我自己写的好多了!

poj 2455 Secret Milking Machine 二分 + dinic求最大流



//poj 2455 Secret Milking Machine

//二分 + 最大流



//题意:

//输入第一行3个数为结点数,边数,路径数

//接下来为每条边的信息

//要求找出对应路径数中每条边都尽量小,输出这些路径中的边最长边为多少

//每条边只能用一次



//思路:

//对路径中的最长边进行二分,之后用最大流在符合边长限制的边中找看看有几条从

//起点 1 到终点 n的路,若大于限制的路径数就向左二分,反之向右二分



//注意:

//dinic:广搜时记得要有容量的且端点没分过层的才可以进行分层;

//深搜时求流向下一结点的流量为流到该结点的流量flow减去已经从该结点

//流出去的流量tmp,并和现在遍历到得边的容量相比,取小的往下一节点流,

//m = min(edge[i].cap, flow - tmp);最后若从目前结点没有流量流出,

//则把它的层标记为-1,即下次不能流到这一结点了



//具体看代码注释



#define infile freopen("in.txt", "r", stdin);

#include <stdio.h>

#include <string.h>

#include <queue>

using namespace std;



const int INF = 1<<30;

const int N = 205;



struct LINE

{

    int st, ed, dis;

}line[N*N];



struct EDGE

{

    int st, ed, dis, cap, next;

}edge[2*N*N];



int eid;

int head[N], level[N];



void add_edge(int st, int ed, int dis)

{

    edge[eid].st = st;

    edge[eid].ed = ed;

    edge[eid].dis = dis;

    edge[eid].cap = 1;      //每条边只能用一次,所以容量为 1

    edge[eid].next = head[st];

    head[st] = eid++;



    edge[eid].st = ed;      //双向边,建反向边

    edge[eid].ed = st;

    edge[eid].dis = dis;

    edge[eid].cap = 1;

    edge[eid].next = head[ed];

    head[ed] = eid++;

}



bool bfs(int source, int sink)

{

    memset(level, 0, sizeof(level));

    queue<int>que;

    que.push(1);

    level[1] = 1;

    while(!que.empty())

    {

        int now = que.front();

        que.pop();

        for(int i = head[now]; i != -1; i = edge[i].next)

        {

            int ed = edge[i].ed;

            if(edge[i].cap > 0 && level[ed] == 0)   //边要有容量且结点还没被分层才对它分层

            {

                level[ed] = level[now] + 1;

                que.push(ed);

            }

        }

    }

    return level[sink];

}



int dfs(int now, int sink, int flow)

{

    if(now == sink)

        return flow;

    int tmp = 0, f;

    for(int i = head[now]; i != -1; i = edge[i].next)

    {

        int ed = edge[i].ed;

        int m = min(edge[i].cap, flow - tmp);   //流到下一结点的流量



        //流到now结点的流量为flow,从now流出去的流量为tmp

        //所以个人觉得这里的 tmp < flow 可以不用,因为上面的最小值让

        if(level[now] + 1 == level[ed] && edge[i].cap > 0 &&

           tmp < flow && (f = dfs(ed, sink, m)))

        {

            edge[i].cap -= f;

            edge[i^1].cap += f;

            tmp += f;

        }

    }

    if(tmp == 0)

        level[now] = -1;

    return tmp;

}



int dinic(int source, int sink)

{

    int flow = 0;

    while(bfs(source, sink))    //广搜进行分层

        flow += dfs(source, sink, INF); //深搜找最大流

    return flow;

}



void binarySearch(int n_node, int n_edge, int limit, int high)

{

    int low = 1;

    while(low < high)   //对路径的最长边二分

    {

        eid = 0;

        memset(head, -1, sizeof(head));

        int mid = low + (high-low)/2;

        for(int i = 0; i < n_edge; ++i)

            if(line[i].dis <= mid)      //把符合要求的边都加入邻接表中

                add_edge(line[i].st, line[i].ed, line[i].dis);

        int flow = dinic(1, n_node);

        if(flow >= limit)

            high = mid;

        else

            low = mid + 1;

    }

    printf("%d\n", low);

}



int main()

{

    //infile

    int n_node, n_edge, limit;

    while(scanf("%d%d%d", &n_node, &n_edge, &limit) != EOF)

    {

        int m = 0;

        for(int i = 0; i < n_edge; ++i)

        {

            scanf("%d%d%d", &line[i].st, &line[i].ed, &line[i].dis);

            m = m < line[i].dis ? line[i].dis : m;  //记录最长边

        }

        binarySearch(n_node, n_edge, limit, m);

    }

    return 0;

}

你可能感兴趣的:(dinic)