poj 2391 floyed+二分枚举+最大流

传送门

题意:n个牛栏,里面有若干牛,并且有它自己的容量,下雨,牛要避雨,牛栏之间走需要时间,问所有牛都避雨的最少时间。

思路:首先floyd遍,得到两牛栏的最小时间,然后二分枚举时间,看是否达到最大流,想法就这么简单。

感想:这题有点卡时间,我坚持了很久的EK算法终于在这给跪了并且是怎么都起不来的这种状况。。。。于是今早开始看dinic,随便看了看,由于A题心切,就直接照着模版改编着敲了,之后一直wa,知道是long long 范围了还是wa,后来才发现,一个地方值赋错了。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 1<<30
using namespace std;
long long int mm=100000000000000ll;
int m,n,cow[510],con[510],csum,consum;
int fst[510],next[200000],node[200000],c[200000];
int f[200000],en,pre[520],lu[510],dis[510];
int low[520];
long long int l[200000],a[510][510];
bool block[510];
void init()
{
    int u,v;
    long long int len;
    csum=0;
    consum=0;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
    {
        scanf("%d%d",&cow[i],&con[i]);
        csum+=cow[i];
        consum+=con[i];
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            a[i][j]=mm;
        }
        a[i][i]=0;
    }
    for(int i=0; i<m; i++)
    {
        scanf("%d%d%lld",&u,&v,&len);
        if(a[u][v]>len)
        {
            a[u][v]=a[v][u]=len;
        }
    }
    for(int x=1; x<=n; x++)
    {
        for(int y=1; y<=n; y++)
        {
            for(int z=1; z<=n; z++)
            {
                if(a[y][z]>a[y][x]+a[x][z])
                {
                    a[y][z]=a[y][x]+a[x][z];
                }
            }
        }
    }
}
void add(int u,int v,int cc,long long int la)
{
    next[en]=fst[u];
    fst[u]=en;
    node[en]=v;
    c[en]=cc;
    l[en]=la;
    en++;
}
void build(long long int la)
{
    memset(fst,-1,sizeof(fst));
    en=0;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(a[i][j]>la)continue;
            add(i,j+n,maxn,a[i][j]);
            add(j+n,i,0,a[i][j]);
        }
        add(0,i,cow[i],0);
        add(i,0,0,0);
        add(i+n,2*n+1,con[i],0);
        add(2*n+1,i+n,0,0);
    }
}
void lays(int s,int t)
{
    int q[3000],front=0,end=0;
    for(int i=s; i<=t; i++)dis[i]=maxn;
    q[end++]=s;
    dis[s]=0;
    while(front<end)
    {
        int u=q[front++];
        for(int i=fst[u]; i!=-1; i=next[i])
        {
            int v=node[i];
            if(dis[v]==maxn&&c[i]>f[i])
            {
                dis[v]=dis[u]+1;
                q[end++]=v;
            }
        }
    }
}
int dinic(int s,int t)
{
    int flow=0;
    memset(f,0,sizeof(f));
    memset(block,0,sizeof(block));
    memset(low,0,sizeof(low));
    lays(s,t);
    int top=s;
    while(dis[t]!=maxn)
    {
        bool flag=false;
        low[s]=maxn;
        int i,v;
        for(i=fst[top]; i!=-1; i=next[i])
        {
            v=node[i];
            if(c[i]>f[i]&&dis[v]==dis[top]+1&&!block[v])
            {
                flag=true;
                break;
            }
        }
        if(flag)
        {
            low[v]=c[i]-f[i];
            if(low[v]>low[top])low[v]=low[top];
            pre[v]=top;
            top=v;
            lu[v]=i;
            if(top==t)
            {
                flow+=low[t];
                int j=top;
                while(j!=s)
                {
                    int k=lu[j];
                    f[k]+=low[t];
                    f[k^1]-=low[t];
                    j=pre[j];
                }
                top=s;
                memset(low,0,sizeof(low));
            }
        }
        else
        {
            block[top]=1;
            if(top!=s)top=pre[top];
        }
        if(block[s])
        {
            lays(s,t);
            memset(block,0,sizeof(block));
        }
    }
    return flow;
}
void solve()
{
    long long int ans=mm;
    long long int high=mm,low=0,mid;
    while(low<=high)
    {
        mid=(low+high)/2;
        build(mid);
        int aaa=dinic(0,2*n+1);
        if(aaa==csum)
        {
            ans=mid;
            high=mid-1;
        }
        else low=mid+1;
    }
    if(ans==mm)cout<<-1<<endl;
    else cout<<ans<<endl;
}
int main()
{
    init();
    if(csum>consum)
    {
        cout<<-1<<endl;
        return 0;
    }
    solve();
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值