poj2391(floyd+二分+网络流判定)

5 篇文章 0 订阅
1 篇文章 0 订阅

有N块田,每块田下雨时可以容纳a[i]头牛,每块田现在有b[i]头牛,给出一些田与田之间的无向路径及距离,问下雨时最少多少时间后所有牛都能到避雨的地方。


首先floyd求出任意两点的最短距离,然后二分时间,加进去小于时间的边

建图,左边的点代表牛,右边的点代表田地,每一条流表示,一头牛进入了哪一个田地。


真练debug能力啊

1.位运算,运算等级太低,必须加(),毕竟多加不会错,小心驶得万年船啊

2.搞两个inf,有不同的用途



#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<iostream>
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long ll;
const int inf=1<<29;
const ll INF=1ll<<59;
const int N=205;
struct aa
{
	int pre,to,cap,flow;
}edge[N*N*2];
int head[N*2],tot;//edge
int n,p,now[N],can[N];//information
ll a[N][N],ans;//map
int lev[N*3],s,t;//dinic
void addedge(int x,int y,int z)
{
	edge[++tot].to=y;edge[tot].pre=head[x];edge[tot].cap=z;head[x]=tot;
	edge[++tot].to=x;edge[tot].pre=head[y];edge[tot].cap=0;head[y]=tot;
}
bool bfs()
{
	memset(lev,0,sizeof(lev));
	lev[s]=1;queue<int> q;q.push(s);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for (int i=head[u];i;i=edge[i].pre)
		if (edge[i].cap>edge[i].flow&&!lev[edge[i].to])
		{
			lev[edge[i].to]=lev[u]+1;
			if (edge[i].to==t) return true;
			q.push(edge[i].to);
		}
	}
	return false;
}
int dfs(int u,int maxflow)
{
	if (u==t||maxflow==0) return maxflow;
	int ans=0;
	for (int i=head[u];i;i=edge[i].pre)
	if (lev[edge[i].to]==lev[u]+1) 
	{
		int flow=dfs(edge[i].to,min(edge[i].cap-edge[i].flow,maxflow));
		ans+=flow;
		maxflow-=flow;
		edge[i].flow+=flow;
		edge[((i-1)^1)+1].flow-=flow;
//异或的外面必须加括号,调了一上午。。,异或等位运算的优先级太低,必需注意括号的问题 !!!!!!!
		if(maxflow==0) return ans;
	}
	return ans;
}
int work(ll limit)//每一次运行,重新加边
{
	int ans=0;
	memset(head,0,sizeof(head));
	memset(edge,0,sizeof(edge));
	tot=0;s=0,t=n*2+1;
	for (int i=1;i<=n;i++) addedge(s,i,now[i]),addedge(i+n,t,can[i]);
	for (int i=1;i<=n;i++) 
		for (int j=1;j<=n;j++)
		if (a[i][j]<=limit) addedge(i,j+n,inf);
	while (bfs()) ans+=dfs(s,inf);
	return ans;
}
int main()
{
	scanf("%d%d",&n,&p);
	int x,y,total=0;ll z;
	for (int i=1;i<=n;i++) scanf("%d%d",&now[i],&can[i]),total+=now[i];
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++) a[i][j]=a[j][i]=INF;//初始化
	for (int i=1;i<=p;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		if(z<a[x][y]) a[x][y]=a[y][x]=z;
	}
	ll r=0; 
	for (int k=1;k<=n;k++)
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++) 
			{
				if (a[i][j]>a[i][k]+a[k][j])a[i][j]=a[i][k]+a[k][j];
				if (r<a[i][j]&&a[i][j]!=INF)r=a[i][j];//这个地方绝不能等于INF????为什么
			}
	ll l=0,mid;ans=-1;r++;
	while (l<r)
	{
		mid=(l+r)>>1;
		if (work(mid)<total) l=mid+1; 
		else ans=r=mid;
	}
	printf("%lld\n",ans);
	return 0;
} 


总结:

这题的建图还是很显然的,关键是问最小时间,要保证满流,由此分析就可以二分时间,以二分的时间为限制加边,跑网络流。//这里的网络流就用来判定的






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值