poj 2455 Secret Milking Machine 二分+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;
}
原文地址:https://www.cnblogs.com/gabo/p/2638817.html