P2402 奶牛隐藏(floyd+拆点二分网络流)

, 因为奶牛可以同时移动,所以费用流肯定不行的

那似乎必须要二分答案了,要不然最大流没这个功能啊!!

我们检测 m i d mid 时间内能否满足条件

一开始肯定想到这样建图

源点连向每个田,流量为一开始田的奶牛

每个田连向汇点,流量为能容纳的奶牛,流向汇点代表住在这个牛棚

两地的距离如果小于 m i d mid ,连边,边权 i n f inf

跑最大流检验满流即可

但是有问题,比如1能到2,2能到3,推出1也能到3,但实际上1到不了3

. . . . 这样建图边有传递性....所以拆点

拆分为田地和牛棚

a > b , a b i n f 如果a->b有边,a田地连向b牛棚,流量inf

其余都一样了

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
#define int long long
const int inf=1e18;
int n,m,s,t,ss[maxn],p[maxn],dis[maxn],l[maxn],r[maxn],w[maxn],ju[209][209];
struct edge{
	int to,nxt,flow;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int flow){
	d[++cnt]=(edge){v,head[u],flow},head[u]=cnt;
	d[++cnt]=(edge){u,head[v],0},head[v]=cnt;
}
bool bfs()
{
	for(int i=s;i<=t;i++)	dis[i]=0;
	dis[s]=1;
	queue<int>q; q.push( s );
	while( !q.empty() )
	{
		int u=q.front(); q.pop();
		for(int i=head[u];i;i=d[i].nxt )
		{
			int v=d[i].to;
			if( d[i].flow&&dis[v]==0 )
			{
				dis[v]=dis[u]+1;
				if( v==t )	return true;
				q.push( v );
			}
		}
	}
	return false;
}
int dinic(int u,int flow)
{
	if( u==t )	return flow;
	int res=flow;
	for(int i=head[u];i&&res;i=d[i].nxt )
	{
		int v=d[i].to;
		if( dis[v]==dis[u]+1&&d[i].flow)
		{
			int temp=dinic(v,min(res,d[i].flow) );
			if( temp==0 )	dis[v]=0;
			res-=temp;
			d[i].flow-=temp;
			d[i^1].flow+=temp;
		}
	}
	return flow-res;
}
bool isok( int mid )
{
	s=0,t=n*2+1;
	int sumn=0;
	cnt=1;
	memset(head,0,sizeof(head));
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	{
		if( ju[i][j]<=mid )	add(i,j+n,inf);
	}
	for(int i=1;i<=n;i++)
	{
		add(s,i,ss[i]);
		add(i+n,t,p[i]); sumn+=ss[i];
		add(i,i+n,inf);
	}
	int ans=0;
	while( bfs() )	ans+=dinic(s,inf);
	if( ans!=sumn )	return false;
	return true;
}
signed main()
{
	cin >> n >> m;
	for(int i=1;i<=n;i++)	cin >> ss[i] >> p[i];
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
		if( i==j )	ju[i][j]=0;
		else	ju[i][j]=inf;
	for(int i=1;i<=m;i++)
	{
		cin >> l[i] >> r[i] >> w[i];
		ju[l[i]][r[i]]=ju[r[i]][l[i]]=min( w[i],ju[l[i]][r[i]] );
	}
	for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
		ju[i][j]=min( ju[i][j],ju[i][k]+ju[k][j] );
	int ll=0,rr=inf-1,mid,ans=-1;
	while( rr>=ll )
	{
		mid=ll+rr>>1;
		if( isok(mid) )	rr=mid-1,ans=mid;
		else	ll=mid+1;
	}
	cout << ans;
}

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/108303336