奶牛隐藏题解

题目背景

这本是一个非常简单的问题,然而奶牛们由于下雨已经非常混乱,无法完成这一计算,于是这个任务就交给了你。(奶牛混乱的原因看题目描述)

题目描述

在一个农场里有n块田地。某天下午,有一群牛在田地里吃草,他们分散在农场的诸多田地上,农场由m条无向的路连接,每条路有不同的长度。

突然,天降大雨,奶牛们非常混乱,想要快点去躲雨。已知每个田地都建立有一个牛棚,但是每个牛棚只能容纳一定数量的牛躲雨,如果超过这个数量,那多出的牛只能去别的田地躲雨。奶牛们每移动 1 的距离花费 1 时间,奶牛们想知道它们全部都躲进牛棚,最少需要多少时间。(即最后一头奶牛最少要花多久才能躲进牛棚)。

输入格式

第一行有两个整数,分别代表田地数 n 和道路数 m。

接下来 n 行,每行两个整数,第 (i+1) 行的整数 si,pisi​,pi​ 分别表示第 ii 块田地的牛的数量以及该田地的牛棚最多可以容纳多少牛。

接下来 m 行,每行三个整数 u,v,w代表存在一条长度为 w 的连接 u 和 v 的道路。

输出格式

输出一行一个整数表示所有奶牛全都躲进牛棚所用的最少时间。如果无法使全部奶牛都躲进牛棚,输出 −1。

输入输出样例

输入 #1

3 4 
7 2 
0 4 
2 6 
1 2 40 
3 2 70 
2 3 90 
1 3 120

输出 #1

110
  • 题意

    一张无向图上有n个点,每个点上有s[i]头牛,可以容纳p[i]​头牛,如果有一条边(u,v,w)∈G,那么一头牛可以用w的时间从u到v

    问至少需要多久时间可以使全部的牛躲进牛棚。

  • 题解

    看到让所有的牛进入牛棚,这个时间很明显是可以二分的。因为如果tt的时间所有的牛可以躲进,那么t′>tt′>t还有什么理由躲不进呢。

    那么考虑这些时间有什么特点。假设我们用一个集合S来放所有的dis[i][j],那么如果有一个t∉S,那么如果t可行,S中第一个比它小的数字也同样可行。此时在检验这个数字纯粹是在浪费评测机资源 也不会慢多少

    于是我们把所有的时间存进setset里,在放进一个数组,对这个数组进行二分,此时次数为log⁡2(n^2)。

    于是剩下的检验操作有手就行了。建两排点,一排为牛,另一排为牛棚。超级源点SS连到牛,流量为sisi​,牛棚连到超级汇点T,流量为p[i]​,∀dis[i][j]≤mid的点,连一条从牛i连接到牛棚jj,流量+∞的边。跑一遍最大流,若flow=∑i=1nsi,则此方案可行

  • 相信没有多少人喜欢上面的一通分析吧,那么,你们喜欢的代码来了——

  • 代码

  • #include<bits/stdc++.h>
    using namespace std;
    const int maxn=4010,maxe=100010*2;
    #define int long long 
    struct Graph{
        struct node{
            int v,w,nxt;
        }e[maxe<<1];
        int head[maxn],cur[maxn],tot;
        int dis[maxn];
        int s,t;
        void init(int _s,int _t){s=_s,t=_t;tot=1;memset(head,0,sizeof head);}
        Graph(int _s=0,int _t=0){init(_s,_t);}
        void add(int u,int v,int w){
            //printf("%d %d %d\n",u,v,w);
            e[++tot]=(node){v,w,head[u]},head[u]=tot;
            e[++tot]=(node){u,0,head[v]},head[v]=tot;
        }
        #define v e[i].v
        inline bool bfs(){
            queue<int>q;
            memset(dis,0,sizeof dis);
            memcpy(cur,head,sizeof head);
            dis[s]=1;q.push(s);
            while(q.size()){
                int u=q.front();q.pop();
                for(int i=head[u];i;i=e[i].nxt)
                    if(!dis[v]&&e[i].w){
                        dis[v]=dis[u]+1,q.push(v);
                        if(v==t)return true;
                    }
            }
            return  false;
        }
        int dfs(int u,int flow){
            if(u==t)return flow;
            int rest=flow;
            for(int i=cur[u];i&&rest;i=e[i].nxt){
                if(dis[v]==dis[u]+1&&e[i].w){
                    int tmp=dfs(v,min(rest,e[i].w));
                    rest-=tmp,e[i].w-=tmp,e[i^1].w+=tmp;
                }
                cur[u]=i;
            }
            if(rest==0)dis[u]=-1;
            return flow-rest;
        }
        #undef v
        int dinic(){
            int ans=0;
            while(bfs())
                while(int sth=dfs(s,2e9))
                    ans+=sth;
            return ans;
        }
    }; 
    Graph G;
    int f[300][300];
    int s[300],p[300];
    int n,m,u,v;int w,sum;
    const int inf=199000000900;
    bool check(int x){
    	G.init(0,2*n+1);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			if(f[i][j]<=x||i==j)
    				G.add(i,j+n,inf);
    	for(int i=1;i<=n;i++)G.add(G.s,i,s[i]),G.add(i+n,G.t,p[i]);
    	int tmp=G.dinic();
    	return tmp>=sum;
    }
    set<int>a; 
    vector<int>vec;
    signed main(){
    	memset(f,0x3f,sizeof f);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    		scanf("%lld%lld",&s[i],&p[i]),sum+=s[i];
    	for(int i=1;i<=m;i++)
    		scanf("%d%d%lld",&u,&v,&w),
    		f[u][v]=f[v][u]=min(f[v][u],w);
    	for(int k=1;k<=n;k++)
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=n;j++)
    				f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			if(f[i][j]!=0x3f3f3f3f3f3f3f3f)
    				a.insert(f[i][j]);
    	vec.assign(a.begin(),a.end());
    	int l=0,r=vec.size()-1,ans=-1;
    	for(;l<=r;){
    		int mid=l+r>>1;
    		if(check(vec[mid]))ans=vec[mid],r=mid-1;
    		else l=mid+1;
    	}cout<<ans;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值