bzoj2095【POI2010】Bridges

148 篇文章 0 订阅
38 篇文章 0 订阅

2095: [Poi2010]Bridges

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 396   Solved: 127
[ Submit][ Status][ Discuss]

Description

YYD为了减肥,他来到了瘦海,这是一个巨大的海,海中有n个小岛,小岛之间有m座桥连接,两个小岛之间不会有两座桥,并且从一个小岛可以到另外任意一个小岛。现在YYD想骑单车从小岛1出发,骑过每一座桥,到达每一个小岛,然后回到小岛1。霸中同学为了让YYD减肥成功,召唤了大风,由于是海上,风变得十分大,经过每一座桥都有不可避免的风阻碍YYD,YYD十分ddt,于是用泡芙贿赂了你,希望你能帮他找出一条承受的最大风力最小的路线。

Input

输入:第一行为两个用空格隔开的整数n(2<=n<=1000),m(1<=m<=2000),接下来读入m行由空格隔开的4个整数a,b(1<=a,b<=n,a<>b),c,d(1<=c,d<=1000),表示第i+1行第i座桥连接小岛a和b,从a到b承受的风力为c,从b到a承受的风力为d。

Output

输出:如果无法完成减肥计划,则输出NIE,否则第一行输出承受风力的最大值(要使它最小)

Sample Input

4 4
1 2 2 4
2 3 3 4
3 4 4 4
4 1 5 4

Sample Output

4

HINT

Source




最大流的应用混合图判断欧拉回路+二分答案

每次二分一个值x,把权值大于等于x的边全部去掉。如果一座桥只被去掉一条边就把它视作有向边,如果两条边都没去掉就把它视作无向边。那么问题就转化为判断混合图(有向边和无向边均存在)是否存在欧拉回路。

·无向图和有向图存在欧拉回路的充要条件:

①无向图存在欧拉回路,当且仅当该图所有顶点度数都为偶数且该图连通。

②有向图存在欧拉回路,当且仅当该图所有顶点入度等于出度且该图连通。

先把无向边随机确定一个方向,然后计算每个点的入度和出度,只有在每个点入度与出度的差都为偶数时在可能存在欧拉回路。(想想如果有奇数为什么不可能存在欧拉回路?)

好了,现在图中所有节点入度和出度的差都为偶数了,我们不妨设这个偶数为2x。

现在问题就变成了:修改某些边的方向,让每个点的入度等于出度。这里就要用到最大流模型。首先,有向边不能改变方向,所以在构图是就只添加无向边。那无向图如何构图呢?就按照一开始随机的方向的反方向构图(为什么反方向呢?画个图就明白了),每条边容量1。另外新建源点s、汇点t。对于入度>出度的点u,从s到u连边,容量为x;对于出度>入度的点v,从v到t连边,容量为x(x的含义已经在上面给出说明)。之后,跑一次最大流,如果满流就有欧拉回路,反之就没有欧拉回路。(查看流量分配,把流量为1的边反向,得到的图就满足所有点的入度=出度。想想为什么?)

混合图的欧拉回路问题就这样解决了。




#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define LL long long
#define pa pair<int,int>
#define MAXN 10005
#define MAXM 40005
#define INF 1000000000
using namespace std;
int n,m,s,t,l,r,cnt,ans;
int dis[MAXN],head[MAXN],cur[MAXN],in[MAXN],out[MAXN],u[MAXM],v[MAXM],w1[MAXM],w2[MAXM];
struct edge_type
{
	int next,to,v;
}e[MAXM];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void add_edge(int x,int y,int v)
{
	e[++cnt]=(edge_type){head[x],y,v};head[x]=cnt;
	e[++cnt]=(edge_type){head[y],x,0};head[y]=cnt;
}
inline bool bfs()
{
	queue<int>q;
	memset(dis,-1,sizeof(dis));
	dis[s]=0;q.push(s);
	while (!q.empty())
	{
		int tmp=q.front();q.pop();
		if (tmp==t) return true;
		for(int i=head[tmp];i;i=e[i].next) if (e[i].v&&dis[e[i].to]==-1)
		{
			dis[e[i].to]=dis[tmp]+1;
			q.push(e[i].to);
		}
	}
	return false;
}
inline int dfs(int x,int f)
{
	if (x==t) return f;
	int tmp,sum=0;
	for(int &i=cur[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if (e[i].v&&dis[y]==dis[x]+1)
		{
			tmp=dfs(y,min(f-sum,e[i].v));
			e[i].v-=tmp;e[i^1].v+=tmp;sum+=tmp;
			if (sum==f) return sum;
		}
	}
	if (!sum) dis[x]=-1;
	return sum;
}
inline void dinic()
{
	ans=0;
	while (bfs())
	{
		F(i,1,t) cur[i]=head[i];
		ans+=dfs(s,INF);
	}
}//最大流模板 
inline bool check(int mid)
{
	int sum=0;
	cnt=1;
	memset(head,0,sizeof(head));
	memset(in,0,sizeof(in));
	memset(out,0,sizeof(out));
	F(i,1,m)
	{
		if (w1[i]<=mid) out[u[i]]++,in[v[i]]++;//计算入度和出度 
		if (w2[i]<=mid) add_edge(v[i],u[i],1);//无向边加入图中,方向为假设方向的反方向 
	}
	F(i,1,n) if (abs(in[i]-out[i])&1) return false;//如果入度与出度差为奇数,则不可能存在欧拉回路 
	F(i,1,n)
	{
		int x=(in[i]-out[i])/2;//入度与出度的差除以2 
		sum+=x>0?x:0;
		if (x>0) add_edge(s,i,x);//入度大于出度就从源点向该点连边 
		if (x<0) add_edge(i,t,-x);//入度小于出度就从该点向汇点连边 
	}
	dinic();
	return ans==sum;//判断是否满流 
}//重点理解这个函数 
int main()
{
	n=read();m=read();
	l=INF;r=-INF;s=n+1;t=n+2;
	F(i,1,m)
	{
		u[i]=read();v[i]=read();w1[i]=read();w2[i]=read();
		if (w1[i]>w2[i]) swap(w1[i],w2[i]),swap(u[i],v[i]);
		l=min(l,w1[i]);r=max(r,w2[i]);
	}
	while (l<r)
	{
		int mid=(l+r)>>1;
		if (check(mid)) r=mid;else l=mid+1;
	}
	if (!check(l)) printf("NIE\n");else printf("%d\n",l);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值