POJ 2391 Ombrophobic Bovines

20 篇文章 0 订阅
19 篇文章 0 订阅

POJ 2391 Ombrophobic Bovines

网络流

传送门:POJ


题意

F个牛棚,P条路。每个牛棚有初始牛数,能容纳的最大牛数。
下雨了,牛要避雨。每个牛棚不能超过容纳数上限。
牛棚之间有路,路上没有通过牛的数目限制。
问牛走的最远距离最小是多少。无解输出-1。


思路

关于拆点%%%

这题根poj2112很像,同样构图,然后二分。但是有不同。

  • POJ 2112跟本题很相似,也是二分,但它就没用拆点,本题就用了
    为啥呢?
  • 因为POJ2112上建的图中是一个很明显的二分图,也就是左边的点绝对不会跟左边的点去连边的。而本题中就不一样了,任何点之间都可能有边。
  • 会出现这种情况,1->2->3,这是一条链,而1->2最短路为1,2->3为1,1->3为 2,此时如果我们限制最大距离为1的话,建的新图中必然有1->2,2->3, 然后我们就发现问题了,新图中1和3也会间接的连接起来,而我们显然是不想这么让它流的。
  • 这就需要拆点了,对每个点x,拆成x’和x”,然后x’和x”之间有一条无限容量的边,这样的话,随便多少牛路过这个点都是可以的。
  • 如果i->j这条边符合了距离限制,就加i’->j” 所有的边加完后,建立源点,对所有的x’连边,容量为已经有的牛,汇点的话,就用所有的j”连接汇点,容量就是能容纳的牛的数量。
  • 这样一拆点,就发现之前的问题不复存在了,还是比如1->2->3这个例子,加的边是1’->2”,2’->3” 不会有流从1流到3去,因为加的每条边都流向了汇点

代码

注意一下距离需要用longlong存

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
const int MAXN=507;
const int oo=0x3f3f3f3f;
const long long int loo=4223372036854775807ll;
typedef long long LL;
struct Dinic
{
    struct Edge{
        int from, to, cap, flow;//cap容量 flow流量
        Edge(){}
        Edge(int u, int v, int c, int f){ from=u;to=v;cap=c;flow=f; }
    };
    vector<Edge> edges;//顺序的插入边
    vector<int> G[MAXN];//保存边号
    bool vis[MAXN];//BFS使用
    int d[MAXN];
    int cur[MAXN];
    void init(int n)
    {
        memset(d, 0, sizeof(d));
        for(int i=0;i<n;i++) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from, int to, int cap)
    {
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));//单向边第三个参数写0,双向边写cap
        int t_m=edges.size();
        G[from].push_back(t_m-2);
        G[to].push_back(t_m-1);
    }

    bool BFS(int s, int t)
    {
        memset(vis, 0, sizeof(vis));
        queue<int> Q;
        Q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!Q.empty())
        {
            int x=Q.front();Q.pop();
            for(int i=0;i<G[x].size();i++)
            {
                Edge& e=edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow)//残量网络
                {
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x, int a, int s, int t)
    {
        if(x==t||a==0) return a;
        int flow=0, _f;
        for(int& i=cur[x];i<G[x].size();i++)
        {
            Edge& e=edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(_f=DFS(e.to, min(a, e.cap-e.flow), s, t))>0)
            {
                e.flow+=_f;
                edges[G[x][i]^1].flow-=_f;
                flow+=_f;
                a-=_f;
                if(a==0) break;
            }
        }
        return flow;
    }
    int Maxflow(int s, int t)
    {
        int flow=0;
        while(BFS(s, t))
        {
            memset(cur, 0, sizeof(cur));
            flow+=DFS(s, oo, s, t);
        }
        return flow;
    }
};

LL ddd[MAXN][MAXN];
Dinic dinic;
int main()
{
    int f, p;
    while(cin>>f>>p)
    {
        vector<int> cow1;
        vector<int> cow2;
        int should_flow=0;
        for(int i=1;i<=f;i++)
        {
            int t1, t2;
            scanf("%d%d", &t1, &t2);
            cow1.push_back(t1);
            cow2.push_back(t2);
            ddd[i][i]=0;
            should_flow+=t1;
        }
        memset(ddd, 0x3f, sizeof(ddd));
        for(int i=0;i<p;i++)
        {
            LL t1, t2, t3;
            scanf("%lld%lld%lld", &t1, &t2, &t3);
            ddd[t2][t1]=ddd[t1][t2]=min(t3, ddd[t1][t2]);
        }
        for(int k=1;k<=f;k++)
        {
            for(int i=1;i<=f;i++)
            {
                for(int j=1;j<=f;j++)
                {
                    if(i==j) continue;
                    ddd[i][j]=min(ddd[i][k]+ddd[k][j], ddd[i][j]);
                }
            }
        }
        LL l=0, r=loo, res=-1;
        while(l<=r)
        {
            LL mid=(l+r)/2;
            dinic.init(500);
            const int sup_t=451, sup_s=450;
            for(int i=0;i<cow1.size();i++)
            {
                dinic.AddEdge(sup_s, i+1, cow1[i]);
            }
            for(int i=0;i<cow2.size();i++)
            {
                dinic.AddEdge(i+1+205, sup_t, cow2[i]);
            }
            for(int i=1;i<=f;i++)
            {
                dinic.AddEdge(i, i+205, oo);
            }
            for(int i=1;i<=f;i++)
            {
                for(int j=1;j<=f;j++)
                {
                    if(ddd[i][j]<=mid)
                    {
                        dinic.AddEdge(i, j+205, oo);
                    }
                }
            }
            if(dinic.Maxflow(sup_s, sup_t)==should_flow)
            {
                res=mid;
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        printf("%lld\n", res);
    }
    //system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值