POJ 2391 Ombrophobic Bovines (二分答案+floyd+最大流)

<题目链接>

题目大意:

给定一个有$n$个顶点和$m$条边的无向图,点$i$ 处有$A_i$头牛,点$i$ 处的牛棚能容纳$B_i$头牛,每条边有一个时间花费$t_i$(表示从一个端点走到另一个端点所需要的时间),求一个最短时间T使得在T时间内所有的牛都能进到某一牛棚里去。$(1 <= N <= 200, 1 <= M <= 1500,0 <= A_i <= 10^3, 0 <= B_i <= 10^3, 1 <= Dij <= 10^9)$。

解题分析:
很明显要二分答案,二分枚举最短的时间,然后用最大流去进行验证。将每个点$i$拆成两个点$i$和$i+n$,然后就是对于每次枚举的答案,进行重新建图,然后跑最大流,如果最大流等于所有牛的数量,则说明所有的牛都有牛棚能够停留,符合条件。建图的方法就是:超级源点向所有的点$i$连一条边,容量为i点初始牛的数量,所有点$i+n$向汇点连一条边,容量为该点牛的容量。因为点的数量很少,floyd预处理出任意两点之间的最短距离,然后对于每次枚举的时间T,最短距离小于等于T的边就加入网络,建好图之后,跑一遍最大流,进行判断。

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

typedef long long ll;
const int N = 505 ;
const ll INF = 1e18;
int n,m,st,ed;
int num[N],capa[N];
ll g[N][N],fullFlow; 

template<typename T>
inline void read(T&x){
    x=0;int f=1;char c=getchar();
    while(c<'0' || c>'9'){ if(c=='-')f=-1;c=getchar(); }
    while(c>='0' && c<='9'){ x=x*10+c-'0';c=getchar(); }
    x*=f;
}
struct Dinic
{
    struct edge{ int from,to;ll cap,flow; };     
    vector<edge>es;
    vector<int>G[N];
    bool vis[N];
    int g[N],iter[N];
    void init(int n){
        for(int i=0; i<=n+10; i++)G[i].clear();
        es.clear();
    }
    void addedge(int from,int to,ll cap){
        es.push_back((edge){from,to,cap,0});  
        es.push_back((edge){to,from,0,0});
        int x=es.size();     
        G[from].push_back(x-2);   
        G[to].push_back(x-1);
    }
    bool BFS(int s,int t){     
        memset(vis,0,sizeof(vis));
        queue <int> q;
        vis[s]=1;
        g[s]=0;
        q.push(s);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=0; i<G[u].size(); i++){
                edge &e=es[G[u][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to]=1;
                    g[e.to]=g[u]+1;
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int u,int t,ll f){
        if(u==t||f==0)return f;
        int numflow=0,d;
        for(int &i=iter[u]; i<G[u].size(); i++){
            edge &e=es[G[u][i]];
            if(g[u]+1==g[e.to]&&(d=DFS(e.to,t,min(f,e.cap-e.flow)))>0){
                e.flow+=d;     
                es[G[u][i]^1].flow-=d;   
                numflow+=d;  
                f-=d;     
                if(f==0)break;
            }
        }
        return numflow;
    }
    int Maxflow(int s,int t){
        int flow=0;
        while(BFS(s,t)){
            memset(iter,0,sizeof(iter));
            int d=0;
            while(d=DFS(s,t,INF))flow+=d;
        }
        return flow;
    }
}dinic;

bool check(ll T){    //最长的时间限制
    dinic.init(N);
    for(int i=1;i<=n;i++){
        dinic.addedge(st,i,num[i]);   //源点向每个点连一条边,容量为这个点的牛的数量
        dinic.addedge(i+n,ed,capa[i]);   //每个点拆点之后的点向汇点连一条边,容量为这个点能够容纳牛的数量
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(g[i][j]<=T)      //将符合条件的边加上
                dinic.addedge(i,j+n,INF);
    return dinic.Maxflow(st,ed) == fullFlow;  //判断是否满流,即是否所有的牛都有牛棚住
}
inline void Floyd(){    //floyd求出任意两点之间的最短距离
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(g[i][j]>g[i][k]+g[k][j])
                    g[i][j]=g[i][k]+g[k][j];
}
inline void Init(){
    fullFlow = 0;
    st=0,ed=2*n+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            g[i][j]=(i==j)?0:INF;     //初始化任意两点之间的距离
    for(int i=1;i<=n;i++){
        read(num[i]);read(capa[i]);
        fullFlow +=num[i];      //记录所有牛的数量
    }
    for(int i=1;i<=m;i++){
        int u,v;ll w;
        read(u);read(v);read(w);
        g[u][v]=g[v][u]=min(g[u][v],w);
    }
    Floyd();
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        Init();
        ll l=0,r=0;//二分的上下界
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(g[i][j]<INF)
                    r=max(r,g[i][j]);    //找到时间的上界
        ll ans=-1;      //二分答案
        while(l<=r){
            ll mid=(l+r)>>1;
            if(check(mid))ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld
",ans);
    }
}
原文地址:https://www.cnblogs.com/00isok/p/10644061.html