【BZOJ2095】【POI2010】Bridge 网络流

题目大意

​  给你一个无向图,每条边的两个方向的边权可能不同。要求找出一条欧拉回路使得路径上的边权的最大值最小。无解输出"NIE"。
  \(2\leq n\leq 1000,1\leq m\leq 2000\)

题解

​  我们先二分答案\(ans\),把边权大于\(ans\)的边删掉。

​  现在图中还剩下一些有向边和一些无向边,也就是说这是一个混合图。

​  混合图的欧拉回路怎么求?

​  先把无向边定向(方向任意),求出每个点的出度\(d1_i\)和入度\(d2_i\)。如果存在点\(i\)使得\(|d1_i-d2_i|\)为奇数,则无解。因为你怎么反向都不可能把\(d1_i-d2_i\)变成\(0\)

​  然后把无向边按定向的反方向在图中连边,容量为\(1\)。对于一个点\(i\),如果\(d1_i>d2_i\),则连边\(i\text{->}T\),容量为\(\frac{d1_i-d2_i}{2}\),否则连边\(S\text{->}i\),容量为\(\frac{d2_i-d1_i}{2}\)

​  最后跑一次最大流。如果满流就有解,否则无解。

  还要用并查集判一下是不是连通图。

​  为什么这是对的?每流过一条边就表示把这条边反向。对这个网络求最大流就是调整尽可能多的边。流量平衡就表示一个点的入度和出度相同。

这里写图片描述

​  这个图把边定向得到

​  这里写图片描述

​  建图后跑最大流可以得到

  这里写图片描述

​  把满流边反向后得到

  这里写图片描述

​  这就是一个欧拉回路了

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
struct list
{
	int v[100010];
	int w[100010];
	int t[100010];
	int h[1010];
	int n;
	void clear()
	{
		memset(h,0,sizeof h);
		n=0;
	}
	void add(int x,int y,int z)
	{
		n++;
		v[n]=y;
		w[n]=z;
		t[n]=h[x];
		h[x]=n;
	}
};
list l;
void add(int x,int y,int z)
{
	l.add(x,y,z);
	l.add(y,x,0);
}
int d[1010];
int S,T;
int bfs()
{
	memset(d,-1,sizeof d);
	queue<int> q;
	q.push(S);
	d[S]=0;
	int x,i;
	while(!q.empty())
	{
		x=q.front();
		q.pop();
		for(i=l.h[x];i;i=l.t[i])
			if(l.w[i]&&d[l.v[i]]==-1)
			{
				d[l.v[i]]=d[x]+1;
				if(l.v[i]==T)
					return 1;
				q.push(l.v[i]);
			}
	}
	return 0;
}
int op(int x)
{
	return ((x-1)^1)+1;
}
int dfs(int x,int flow)
{
	if(x==T)
		return flow;
	int c,s=0,i;
	for(i=l.h[x];i;i=l.t[i])
		if(l.w[i]&&d[l.v[i]]==d[x]+1)
		{
			c=dfs(l.v[i],min(flow,l.w[i]));
			s+=c;
			flow-=c;
			l.w[i]-=c;
			l.w[op(i)]+=c;
			if(!flow)
				break;
		}
	return s;
}
int f[1010];
int find(int x)
{
	return f[x]==x?x:f[x]=find(f[x]);
}
int lx[2010],ly[2010],w1[2010],w2[2010];
int d1[2010],d2[2010];
int c[2010];//方向 
int n,m;
int abs(int x)
{
	return x>0?x:-x;
}
int check(int p)
{
	memset(d1,0,sizeof d1);
	memset(d2,0,sizeof d2);
	int i;
	for(i=1;i<=n;i++)
		f[i]=i;
	for(i=1;i<=m;i++)
	{
		if(p<w1[i]&&p<w2[i])
			return 0;
		if(p>=w1[i])
		{
			c[i]=0;
			d1[lx[i]]++;
			d2[ly[i]]++;
			f[find(lx[i])]=find(ly[i]);
		}
		else
		{
			c[i]=1;
			d1[ly[i]]++;
			d2[lx[i]]++;
			f[find(lx[i])]=find(ly[i]);
		}
	}
	for(i=1;i<=n;i++)
	{
		if(abs(d1[i]-d2[i])&1)
			return 0;
		if(i>1&&find(i)!=find(i-1))
			return 0;
	}
	l.clear();
	S=n+1;
	T=n+2;
	for(i=1;i<=m;i++)
		if(p>=w1[i]&&p>=w2[i])
			add(ly[i],lx[i],1);
//		else
//			add(lx[i],ly[i],1);
	int s=0,ans=0;
	for(i=1;i<=n;i++)
		if(d1[i]>d2[i])
		{
			add(i,T,(d1[i]-d2[i])/2);
			s+=(d1[i]-d2[i])/2;
		}
		else if(d1[i]<d2[i])
			add(S,i,(d2[i]-d1[i])/2);
	while(bfs())
		ans+=dfs(S,0x7fffffff);
	return ans==s;
}
int main()
{
//	freopen("bzoj2095.in","r",stdin);
	scanf("%d%d",&n,&m);
	int i;
	for(i=1;i<=m;i++)
		scanf("%d%d%d%d",&lx[i],&ly[i],&w1[i],&w2[i]);
	int l=1,r=1001;
	int mid;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(check(mid))
			r=mid;
		else
			l=mid+1;
	}
	if(l>1000)
		printf("NIE\n");
	else
		printf("%d\n",l);
	return 0;
}
posted @ 2018-03-05 18:32  ywwyww  阅读(321)  评论(0编辑  收藏  举报