POJ2391 Ombrophobic Bovines(二分+拆点+最大流)

链接:http://poj.org/problem?id=2391
题意:
有n个点,m条边(无向边),先输入n个点的信息,前面那个是这个点内已经有的牛,后面的是这个点最多可以容纳几头牛。m条边就是起点,终点,所需时间。问:所有奶牛都在点内安排好,所需的最少时间。如果无解输出-1。
根据题意,我们会想到一个思路,就是二分最大距离,然后跑最大流,这是一个初级思路,我写了这个之后发现是错的,仔细思考以后发现这样一种情况。不同的点间接连接着,我们二分的最大距离是x的话,这些点的距离最大确实都不超过x,但是加起来却超过了x,这样不符合我们的要求。我看了别人的博客,学习到一个思路就是拆点。
把每个点拆成入点和出点两种。那么我们在建图的时候,可以这样建图。
源点->每个点的入点 权值为这个点已存在的牛。
每个点的出点->汇点 权值为这个点可以容纳的牛。
每个点的入点->自己的出点 权值为所有牛的总数,因为每条路可以容纳无数牛。
最关键的是点与点之间的路径怎么建立。我们的目的是为了避免间接相连,也就是说,每条路径存的都要是直达。从前面的边可以看出,从每个点的出点出去之后都是汇点,而且,每个点的入点都是从源点得到的。所以要直达的话,点之间的路径就是符合条件的边(u,v)的(u)入点->(v)出点。
知道这些条件以后,就是二分距离,然后最大流了。
这题还有一个地方需要注意,就是一开始在图中设置的距离的最大值的问题,我们考虑有200个点,每个点 i 只和i + 1相连,而且距离是最大值1e9,那么点 1 和点200的距离就是199*1e9,差不多200*1e9就是2e11。因为一开始最大值设置的不对,一直错没有发现原因。
Accepted 1708K 282MS G++ 2850B

#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<map>
#include<iostream>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<stack>
#include<cctype>
#include<set>
#include<cassert>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF=1e9+7;
typedef pair<int,int> pii;
#define in(x) (x)
#define out(x) (x+n)
struct node{
    int cow,last;
}val[210];
int n,m,Cow,st,ed;
ll pic[210][210];
void floyd(){
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(pic[i][j]>pic[i][k]+pic[k][j]){
                    pic[i][j]=pic[i][k]+pic[k][j];
                }
            }
        }
    }
}
int F[410][410];
int d[410];
void build(ll v){
    st=0;ed=n+n+1;
    memset(F,0,sizeof F);
    for(int i=1;i<=n;i++){
        F[st][in(i)]=val[i].cow;
        F[out(i)][ed]=val[i].last;
        F[in(i)][out(i)]=Cow;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(pic[i][j]<=v){
                F[in(i)][out(j)]=Cow;
            }
        }
    }
}
bool BFS(){
    queue<int> Q;
    memset(d,-1,sizeof d);
    d[st]=0;Q.push(st);
    while(!Q.empty()){
        int s=Q.front();Q.pop();
        for(int i=1;i<=ed;i++){
            if(F[s][i]>0&&d[i]<0){
                d[i]=d[s]+1;Q.push(i);
            }
        }
    }
    return d[ed]>0;
}
int DFS(int s,int t,int flow){
    if(s==t||flow==0)return flow;
    int ans=0;
    for(int i=1;i<=ed;i++){
        if(F[s][i]>0&&d[i]==d[s]+1){
            int ff=DFS(i,t,min(flow,F[s][i]));
            if(ff>0){
                F[s][i]-=ff;
                F[i][s]+=ff;
                ans+=ff;
                flow-=ff;
                if(!flow)break;
            }
        }
    }
    if(!ans)d[s]=-1;
    return ans;
}
int dinic(ll v){
    build(v);
    int ans=0;
    while(BFS()){
        ans+=DFS(st,ed,INF);
    }
    return ans;
}
int main(){
//    freopen("D://input.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&val[i].cow,&val[i].last);
        Cow+=val[i].cow;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i!=j)pic[i][j]=2e11;
            else pic[i][j]=0;
        }
    }
    for(int i=1;i<=m;i++){
        int a,b,c;scanf("%d%d%d",&a,&b,&c);
        if(pic[a][b]>c)pic[a][b]=pic[b][a]=c;
    }
    floyd();
    ll l=0,r=0,ans=-1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(pic[i][j]!=2e11)
                r=max(r,pic[i][j]);
        }
    }
    while(l<=r){
        ll m=(l+r)>>1;
        if(dinic(m)==Cow){
            ans=m;r=m-1;
        }
        else l=m+1;
    }
    printf("%I64d\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值