POI2010 Mos-Bridges(二分答案+欧拉回路+网络流)
【题目描述】
YYD 为了减肥,他来到了瘦海,这是一个巨大的海,海中有 n nn 个小岛,小岛之间有 m mm 座桥连接,两个小岛之间不会有两座桥,并且从一个小岛可以到另外任意一个小岛。现在 YYD 想骑单车从小岛 1 11 出发,骑过每一座桥,到达每一个小岛,然后回到小岛 1 11。霸中同学为了让 YYD 减肥成功,召唤了大风,由于是海上,风变得十分大,经过每一座桥都有不可避免的风阻碍 YYD,YYD 十分 ddt,于是用泡芙贿赂了你,希望你能帮他找出一条承受的最大风力最小的路线。
【输入格式】
第一行为两个用空格隔开的整数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。
【输出格式】
如果无法完成减肥计划,则输出 NIE,否则第一行输出承受风力的最大值(要使它最小)
【样例输入】
4 4
1 2 2 4
2 3 3 4
3 4 4 4
4 1 5 4
【样例输出】
4
【备注】
注意:通过桥为欧拉回路
【题目分析】
要让最大值最小,显然是二分答案的套路,所以我们就去二分这个最大值,将所有不大于这个值的边加入图中,然后检查是否构成欧拉回路。
然后问题也就来了,单纯的无向图和有向图的欧拉回路好判,但如何在混合图中去判欧拉回路?
VANVAN没想到就是网络流啊。。。。。。
首先我们将所有无向边任意定向,统计一下所有点出入度数,如果二者奇偶性不同,那么无解(将一条无向边反向后出入度变化2),所以每个点向源汇点连一条自身出入度差值的一半的边,表示需要转向这么多次。
那么问题就转化为匹配问题,如果最后最大流流满,那么就一定对应原图中存在欧拉回路。
【代码~】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=1e3+10;
const int MAXM=4e3+10;
const int MAXP=1e6+10;
const int INF=0x3f3f3f3f;
int Read()
{
int i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
int n,m,cnt;
int s=0,t=1001;
int minn=INF,maxx=0;
int head[MAXN],depth[MAXN],cur[MAXN];
int nxt[MAXP],to[MAXP],w[MAXP];
int from[MAXM],too[MAXM],w1[MAXM],w2[MAXM];
int du[MAXN],tot;
void Add(int x,int y,int z)
{
nxt[cnt]=head[x];
head[x]=cnt;
to[cnt]=y;
w[cnt]=z;
cnt++;
}
void add(int x,int y,int z)
{
Add(x,y,z);
Add(y,x,0);
}
bool bfs(){
memset(depth,0,sizeof(depth));
queue<int> q;
q.push(s);
depth[s]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=nxt[i])
{
int v=to[i];
if(w[i]&&depth[v]==0)
{
depth[v]=depth[u]+1;
q.push(v);
}
}
}
if(depth[t]==0)
return false;
return true;
}
int dfs(int u,int dist)
{
if(u==t)
return dist;
for(int &i=cur[u];i!=-1;i=nxt[i])
{
int v=to[i];
if(w[i]&&depth[v]==depth[u]+1)
{
int di=dfs(v,min(dist,w[i]));
if(di>0)
{
w[i]-=di;
w[i^1]+=di;
return di;
}
}
}
return 0;
}
int dinic()
{
int ans=0;
while(bfs())
{
for(int i=s;i<=t;++i)
cur[i]=head[i];
while(int d=dfs(s,INF))
ans+=d;
}
return ans;
}
void build(int mid)
{
memset(head,-1,sizeof(head));
memset(du,0,sizeof(du));
cnt=0;
tot=0;
for(int i=1;i<=m;++i)
{
if(w1[i]<=mid)
--du[from[i]],++du[too[i]];
if(w2[i]<=mid)
add(too[i],from[i],1);
}
for(int i=1;i<=n;++i)
if(du[i]>0)
tot+=du[i]>>1,add(s,i,du[i]>>1);
else
if(du[i]<0)
add(i,t,(-du[i])>>1);
}
bool check(int mid)
{
build(mid);
for(int i=1;i<=n;++i)
if(du[i]&1)
return false;
if(dinic()==tot)
return true;
return false;
}
int main()
{
n=Read(),m=Read();
for(int i=1;i<=m;++i)
{
from[i]=Read(),too[i]=Read(),w1[i]=Read(),w2[i]=Read();
if(w1[i]>w2[i])
swap(w1[i],w2[i]),swap(from[i],too[i]);
minn=min(minn,w1[i]);
maxx=max(maxx,w2[i]);
}
int l=minn,r=maxx;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
r=mid-1;
else
l=mid+1;
}
if(l==maxx+1)
puts("NIE");
else
cout<<l;
return 0;
}