2095: [Poi2010]Bridges
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 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
1 2 2 4
2 3 3 4
3 4 4 4
4 1 5 4
Sample Output
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;
}