P2402

P2402 奶牛隐藏

\(n\) 个点和 \(m\) 条有边权无向边,对于每个点有牛的数量 \(s_i\) 和牛棚容量 \(p_i\)
求问所有牛全部进入牛棚所需的最小时间,无解输出 -1。
\(1\le n\le 200,1\le m\le 1500,1\le w\le 10^{15},1\le s_i,p_i\le 10^{16}\)

看到 \(n\le 200\),可以想到用 Floyd 跑一遍全源最短路。

int edge[N][N];
for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
		edge[i][j]=(i==j?0:INF);
for(int i=1,u,v,w;i<=m;i++){
	u=rd(),v=rd(),w=rd();
	edge[u][v]=min(edge[u][v],w);
	edge[v][u]=min(edge[v][u],w);
}for(int k=1;k<=n;k++)
  for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	  edge[i][j]=min(edge[i][j],edge[i][k]+edge[k][j]);

看到求最少时间,考虑二分答案

while(l<=r){
	mid=(l+r)>>1;
	if(check(mid))r=mid-1,ans=mid;
	else l=mid+1;
}wr(ans?ans:-1);

看到“牛进入牛棚”这样的描述,很容易想到二分图匹配
网络流的建边方法:

  • 首先将每个点分成牛和牛棚两个点。
  • 从源点向每个表示牛的点连一条流量为目标点的牛的数量的边。
  • 从每个表示牛棚的点向汇点连一条流量为当前点牛棚容量的边。
  • 如果两点之间距离小于当前的时间,就连一条流量为两点中表示牛的点的牛的数量的边。
for(int i=1;i<=n;i++)	I(st,i,s[i]);
for(int i=1;i<=n;i++)	I(i+n,ed,p[i]);
for(int i=1;i<=n;i++)
  for(int j=1;j<=n;j++)
	if(edge[i][j]<=lim)
	  I(i,j+n,s[i]);

其实建边的方法就是二分图多重最大权匹配的板子。
建边后直接跑网络流板子即可。

几个细节点:

  1. 二分答案的右边界赋值一定要小于 floyd 的边权初始极值。
  2. 因为有拆点,所以数组一定要开两倍空间。
  3. 注意题目中的边都是无向边。
  4. 存在重边,邻接矩阵取两点之间距离最小值。
  5. 不开 long long 见祖宗。

完整代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=600,M=2e5,INF=3e18;
int rd(){
	int w=0,v=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')v=-1;c=getchar();}
	while(c>='0'&&c<='9')w=(w<<1)+(w<<3)+(c&15),c=getchar();return w*v;
}void wr(int x){if(x<0)putchar('-'),x=-x;if(x>9)wr(x/10);putchar(x%10+'0');}

int fir[N],cnt=1;
struct E{int v,w,nt;}e[M];
void I(int u,int v,int w){
	e[++cnt]=(E){v,w,fir[u]};fir[u]=cnt;
	e[++cnt]=(E){u,0,fir[v]};fir[v]=cnt;
}void cl(){memset(e,0,sizeof(e));memset(fir,0,sizeof(fir));cnt=1;}

int cur[N],d[N],st,ed;
queue <int>q;
bool bfs(){
	memset(d,0,sizeof(d));
	for(int i=0;i<=ed;i++)	cur[i]=fir[i];
	q.push(st);d[st]=1;
	while(q.size()){
		int u=q.front(),V;q.pop();
		for(int i=fir[u];i;i=e[i].nt)
			if(e[i].w&&!d[V=e[i].v])
				q.push(V),d[V]=d[u]+1;
	}return d[ed];
}int dfs(int u,int fl){
	if(u==ed)return fl;int f,V,s=0;
	for(int i=cur[u];i;i=e[i].nt){
		cur[u]=i;
		if(e[i].w&&d[V=e[i].v]==d[u]+1){
			f=dfs(V,min(fl,e[i].w));
			e[i].w-=f;e[i^1].w+=f;
			s+=f;fl-=f;if(!fl)break;
		}
	}if(!s)d[u]=0;return s;
}int dinic(){int ans=0;while(bfs())ans+=dfs(st,INF);return ans;}

int n,m,ans,s[N],p[N];
int s1,s2;
int l=1,r=INF-1,mid;

int edge[N][N];
void floyd(){
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				edge[i][j]=min(edge[i][j],edge[i][k]+edge[k][j]);
}

bool check(int lim){
	cl();
	for(int i=1;i<=n;i++)	I(st,i,s[i]);
	for(int i=1;i<=n;i++)	I(i+n,ed,p[i]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(edge[i][j]<=lim)
				I(i,j+n,s[i]);
	return (dinic()==s1)?1:0;
}

signed main(){
	n=rd(),m=rd();st=0,ed=n*2+1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			edge[i][j]=(i==j?0:INF);
	for(int i=1;i<=n;i++){
		s[i]=rd(),p[i]=rd();
		s1+=s[i];s2+=p[i];
	}for(int i=1,u,v,w;i<=m;i++){
		u=rd(),v=rd(),w=rd();
		edge[u][v]=min(edge[u][v],w);
		edge[v][u]=min(edge[v][u],w);
	}floyd();
	if(s1>s2){wr(-1);return 0;}
	while(l<=r){
		mid=(l+r)>>1;
		if(check(mid))r=mid-1,ans=mid;
		else l=mid+1;
	}wr(ans?ans:-1);
}
posted @ 2022-05-27 20:01  AIskeleton  阅读(20)  评论(0编辑  收藏  举报