poj 2455 最大流+二分

/*
题意:F个区域和P条路径,每个区域有a头牛且能遮蔽b头牛,求其中牛走得最远的路径的最短长度,并且
所有牛都可以进入遮蔽处。

题解:最大流+二分+floyd+拆点;
牛从所在的位置走向遮蔽处必然是走最短路线,因此先用floyd将两两点之间的最短路径先求出,再根据这
个最短路径建立图,源点到每个点加边,权值为牛数a,将区域拆成两个点,并且对相连的区域之间加边,
权值为INF,注意自己和自己也有加上边,就是被这里坑了很久,区域到汇点加边,权值为遮蔽数b;二分
的方法将长于mid的边去除从而建图;然后求出最大流等于牛数目即可。

注意:题目有重边,只记录最小边即可,路径的长度会超过32为,因此用__int64记录路径长度。
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

#define EMAX 505000
#define VMAX 500
#define MAX 0x7FFFFFFFFFFFFFF

const int INF = 0xFFFFFFF;

int head[VMAX],dis[VMAX],cur[VMAX],gap[VMAX],pre[VMAX];
int EN;
struct edge
{
    int to;
    int weight;
    int next;
}e[EMAX];

struct cow
{
    int a,b;
}te[VMAX];

__int64 map[205][205];

void insert(int u,int v,int w) 
{
    e[EN].to = v;
    e[EN].weight = w;
    e[EN].next = head[u];    
    head[u] = EN++;
    e[EN].to = u;
    e[EN].weight = 0;
    e[EN].next = head[v];    
    head[v] = EN++;
}

int sap(int s,int t, int n)//sap求最大流模版
{
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    for(int i=0; i<=n; i++)
        cur[i] = head[i];
    int u = s;
    pre[s] = s;
    int ret = 0;
    int temp = -1;
    gap[0] = n;
    bool flag;
    while(dis[s] < n)
    {
        flag = false;
        for(int &i = cur[u]; i != -1; i = e[i].next)
        {
            int v = e[i].to;
            if(e[i].weight && dis[u] == dis[v] + 1)
            {
                if (temp == -1 || temp>e[i].weight)
                    temp = e[i].weight;
                pre[v] = u;
                u = v;
                if(v == t)
                {
                    ret += temp;
                    for(u = pre[u];v != s;v = u,u = pre[u])
                    {
                        e[cur[u]].weight -= temp;
                        e[cur[u]^1].weight += temp;
                    }
                    temp = -1;
                }
                flag = true;
                break;
            }
        }
        if (flag)
            continue;

        int mindis = n;
        for(int i = head[u]; i != -1 ; i = e[i].next)
        {
            int v = e[i].to;
            if(e[i].weight && mindis > dis[v])
            {
                cur[u] = i;
                mindis = dis[v];
            }
        }
        gap[dis[u]]--;
        if( gap[dis[u]] == 0)
            break;
        dis[u] = mindis+1;
        gap[dis[u]]++;
        u = pre[u];
    }
    return ret;
}

void floyd(int n)
{
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            if (MAX != map[j][i])
                for(int k=1; k<=n; k++)
                    if (map[j][i] + map[i][k] < map[j][k])
                        map[j][k] = map[j][i] + map[i][k];
}

void build(int f,__int64 mid)//建图
{
    memset(head,-1,sizeof(head));
    EN = 0;
    for(int i=1; i<=f; i++)
    {
        insert(0,i,te[i].a);
        insert(f+i,2*f+1,te[i].b);
    }
    for(int i=1; i<=f; i++)
        for(int j=1; j<=f; j++)
            if (map[i][j]<=mid)//去除大于mid的边
                insert(i,f+j,INF);
}

__int64 bin(int f, int sum)//二分
{
    __int64 left=0,right=MAX,mid;
    while (left < right)
    {
        mid = (left+right)/2;
        build(f,mid);
        if (sap(0,2*f+1,2*f+2) < sum)
            left = mid+1;
        else
            right = mid;
    }
    if (right == MAX)
        return -1;
    else
        return right;
}

int main(void)
{
    int f,p,a,b,l;
    while (~scanf("%d%d",&f,&p))
    {
        int sum = 0;
        for(int i=1; i<=f; i++)
            for(int j=1; j<=f; j++)
            {
                if (i != j)
                    map[i][j] = MAX;
                else
                    map[i][j] = 0;
            }
        for(int i=1; i<=f; i++)
        {
            scanf("%d%d",&te[i].a,&te[i].b);
            sum += te[i].a;//求牛的总数
        }
        for(int i=1; i<=p; i++)
        {
            scanf("%d%d%d",&a,&b,&l);
            if (map[a][b] > l)
                map[a][b] = map[b][a] = l;
        }
        floyd(f);
        printf("%I64d\n",bin(f,sum));
    }
    return 0;
}

 

posted @ 2014-03-20 23:59  辛力啤  阅读(229)  评论(0编辑  收藏  举报