天天看点

POJ 2455 Secret Milking Machine && 二分枚举 + 最大流

题目: http://poj.org/problem?id=2455

题意:给定一张无向图,有n个节点p条边,要求在图中从1到n找到t条路径,并且使这t条路径中的最长边最小,输出这个最小的最长边

思路:我们可以二分枚举最小的最长边的长度,然后建图,长度小于等于枚举值的边可以连上,容量为1,建完之后跑最大流,此时最大流的意义是从1到n有几条路径,因此我们二分搜索加最大流便可以求出最长边的最小值。注意有向图和无向图建图时的区别,有重边时不能用邻接矩阵保存边值,此题中也不能只取最小值,而是全部保存

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

const int N = 210;
const int INF = 0x3f3f3f3f;
struct edge
{
    int to, cap, next;
}g[N*1000];
int head[N], iter[N], level[N], d[N*400][3];
int cnt;
void add_edge(int v, int u, int cap)
{
    g[cnt].to = u, g[cnt].cap = cap, g[cnt].next = head[v], head[v] = cnt++;
    g[cnt].to = v, g[cnt].cap = cap, g[cnt].next = head[u], head[u] = cnt++; //无向图时反向边容量不为0
}
bool bfs(int s, int t)
{
    memset(level, -1, sizeof level);
    queue<int> que;
    level[s] = 0;
    que.push(s);
    while(! que.empty())
    {
        int v = que.front(); que.pop();
        for(int i = head[v]; i != -1; i = g[i].next)
        {
            int u = g[i].to;
            if(g[i].cap > 0 && level[u] < 0)
            {
                level[u] = level[v] + 1;
                que.push(u);
            }
        }
    }
    return level[t] == -1;
}
int dfs(int v, int t, int f)
{
    if(v == t) return f;
    for(int &i = iter[v]; i != -1; i = g[i].next)
    {
        int u = g[i].to;
        if(g[i].cap > 0 && level[v] < level[u])
        {
            int d = dfs(u, t, min(f, g[i].cap));
            if(d > 0)
            {
                g[i].cap -= d, g[i^1].cap += d;
                return d;
            }
        }
    }
    return 0;
}
int dinic(int s, int t)
{
    int flow = 0, f;
    while(true)
    {
        if(bfs(s, t)) return flow;
        memcpy(iter, head, sizeof head);
        while(f = dfs(s, t, INF), f > 0)
            flow += f;
    }
}
int main()
{
    int n, p, t;
    while(~ scanf("%d%d%d", &n, &p, &t))
    {
        int maxx = -1, minn = INF;
        for(int i = 0; i < p; i++)
        {
            scanf("%d%d%d", &d[i][0], &d[i][1], &d[i][2]);
            maxx = max(maxx, d[i][2]);
            minn = min(minn, d[i][2]);
        }
        int l = minn, r = maxx, res;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            cnt = 0;
            memset(head, -1, sizeof head);
            for(int i = 0; i < p; i++)
                if(d[i][2] <= mid)
                    add_edge(d[i][0], d[i][1], 1);
            if(dinic(1, n) >= t) r = mid - 1, res = mid;
            else l = mid + 1;
        }
        printf("%d\n", res);
    }
    return 0;
}