题目传送门:https://www.luogu.org/problemnew/show/P2402
题意:
有n个点,m条边,每一个点一开始都有b1i头奶牛,并且都可以容纳b2i头奶牛,但是b1i有可能大于b2i,因此奶牛需要迁徙,迁徙的时间为路程的长度。求最小的时间,若无法完成,输出-1。
思路:
容易想到最短路floyd,因为可以与处理出从i点到j点的最短路(即时间花费)。
然后发现时间好像不能直接求,它具有单调性,再来个二分时间mid,若当前时间允许,则找更短的耗时,否则找更长的耗时。
最后发现可以用网络流求解,就是让不同的牛移动。
构图:
必然要拆点。
[1]源点向每一个点连一条流量为b1i的边,表示一开始这里有b1i头奶牛;
[2]每一个点向汇点连一条流量为b2i的边,表示最多能容纳b2i头奶牛;
[3]每一个点的入点向出点连一条流量为INF的边表示可以有无数头奶牛在这里中转;
[4]每一个点它能去到的点的最短路(即时间花费)小于等于二分的时间mid,则连一条流量为INF的边,表示从i点到j点在规定时间里可以有无数头奶牛走过。
有解的情况是flow=sum(b1的总和,即总的奶牛数),此时保证每一头奶牛都有一个被容纳的点,且每一个点都没有超过它的容量。
代码:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define INF 1e18
#define LL long long
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
using namespace std;
queue<int> f;
struct node{int x,y,next;LL z;} a[100010];
int b1[500],b2[500],last[1000];
LL map[300][300];
int n,m,len=-1,st,ed;
LL s=0,ans=-1;
void ins(int x,int y,LL z)
{
a[++len].x=x;a[len].y=y;a[len].z=z;a[len].next=last[x];last[x]=len;
}
int h[1000];
bool bfs()
{
memset(h,0,sizeof(h));
h[st]=1;
f.push(st);
while(!f.empty())
{
int x=f.front();
for(int i=last[x];i>=0;i=a[i].next)
{
int y=a[i].y;
if(a[i].z>0&&h[y]==0)
{
h[y]=h[x]+1;
f.push(y);
}
}
f.pop();
}
if(h[ed]) return true; else return false;
}
LL dfs(int x,LL f)
{
LL s=0,t;
if(x==ed) return f;
for(int i=last[x];i>=0;i=a[i].next)
{
int y=a[i].y;
if(a[i].z>0&&h[y]==h[x]+1&&f>s)
{
s+=(t=(dfs(y,min(f-s,a[i].z))));
a[i].z-=t;
a[i^1].z+=t;
}
}
if(!s) h[x]=0;
return s;
}
LL dinic()
{
LL sum=0;
while(bfs())
sum+=dfs(st,INF);
return sum;
}
LL work(LL x)
{
len=-1;
memset(last,-1,sizeof(last));
for(int i=1;i<=n;i++)
{
ins(st,i,b1[i]),ins(i,st,0);
ins(i+n,ed,b2[i]),ins(ed,i+n,0);
ins(i,i+n,INF),ins(i+n,i,0);
for(int j=1;j<=n;j++)
if(map[i][j]!=INF&&map[i][j]<=x) ins(i,j+n,INF),ins(j+n,i,0);
}
return dinic();
}
int main()
{
int x,y,z;
LL l=1,r=0,mid;
scanf("%d %d",&n,&m);
st=n*2+1;
ed=st+1;
for(int i=1;i<=n;i++)
{
scanf("%d %d",&b1[i],&b2[i]);
s+=b1[i];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map[i][j]=i==j?0:INF;
for(int i=1;i<=m;i++)
{
scanf("%d %d %d",&x,&y,&z);
if(map[x][y]>z) map[x][y]=map[y][x]=z;
r=MAX(r,map[x][y]);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map[i][j]=MIN(map[i][j],map[i][k]+map[k][j]),r=MAX(r,map[i][j]);
while(l<=r)
{
mid=(l+r)>>1;
if(work(mid)==s) r=mid-1,ans=mid; else l=mid+1;
}
printf("%lld",ans);
}