问题描述
YYD为了减肥,他来到了瘦海,这是一个巨大的海,海中有n个小岛,小岛之间有m座桥连接,两个小岛之间不会有两座桥,并且从一个小岛可以到另外任意一个小岛。现在YYD想骑单车从小岛1出发,骑过每一座桥,到达每一个小岛,然后回到小岛1。霸中同学为了让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
提示
注意:通过桥为欧拉回路
解析
题目要求最大承受风力的最小值,那很明显是二分答案。
二分答案后这个图就会变成一个混合图(也就是既有单向边也有双向边的)。然后只需要判断这个混合图是否为欧拉回路即可。
具体的我们使用网络流模型。设d[i]表示一个点入度减出度的值。对于一条有向边(u,v),就直接d[u]--,d[v]++(即u出度加1,v入度加1)。对于一条无向边(u,v),先假设方向为u->v。如果变向,则会导致d[u]+=2,d[v]-=2。最后,如果是一条欧拉回路,则每一个d[i]都等于0。所以,我们通过改变无向边的方向使任意d[i]等于0。对于d[i]>0,从原点向i连一条容量为d[i]/2的边;对于d[i]<0,向汇点连一条容量为-d[i]/2的边。对于无向边,若当前方向为u到v,则从v向u连一条容量为1的边表示变向。最后如果满流说明是欧拉回路。
对于这道题,二分一个mid,每一条边如果有一个小于等于mid,就作为单向边,两个就作为无向边。建图跑Dinic判断。
代码
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #define N 1002 #define M 2002 #define S 0 #define T n+1 using namespace std; const int inf=1<<30; struct edge{ int u,v,a,b; }e[M]; int head[N],ver[M*10],nxt[M*10],cap[M*10],l; int n,m,i,d[N],dis[N]; int read() { char c=getchar(); int w=0; while(c<'0'||c>'9') c=getchar(); while(c<='9'&&c>='0'){ w=w*10+c-'0'; c=getchar(); } return w; } int abs(int x) { return x<0?-x:x; } void insert(int x,int y,int z) { ver[l]=y; cap[l]=z; nxt[l]=head[x]; head[x]=l; l++; ver[l]=x; cap[l]=0; nxt[l]=head[y]; head[y]=l; l++; } bool bfs() { queue<int> q; memset(dis,-1,sizeof(dis)); q.push(S); dis[S]=0; while(!q.empty()){ int x=q.front(); q.pop(); for(int i=head[x];i!=-1;i=nxt[i]){ int y=ver[i]; if(dis[y]<0&&cap[i]>0){ dis[y]=dis[x]+1; q.push(y); } } } return (dis[T]>0); } int Dinic(int x,int flow) { if(x==T||flow==0) return flow; int ans=0; for(int i=head[x];i!=-1;i=nxt[i]){ int y=ver[i]; if(dis[y]==dis[x]+1&&cap[i]>0){ int a=Dinic(y,min(flow,cap[i])); flow-=a; ans+=a; cap[i]-=a; cap[i^1]+=a; } if(flow==0) break; } if(flow) dis[x]=-1; return ans; } bool check(int x) { memset(head,-1,sizeof(head)); memset(d,0,sizeof(d)); l=0; int ans=0,sum=0; for(int i=1;i<=m;i++){ if(e[i].a<=x) d[e[i].u]--,d[e[i].v]++; if(e[i].b<=x) insert(e[i].v,e[i].u,1); } for(int i=1;i<=n;i++){ if(abs(d[i])%2==1) return 0; if(d[i]<0) insert(i,T,-d[i]/2); else insert(S,i,d[i]/2),sum+=d[i]/2; } while(bfs()) ans+=Dinic(S,inf); return (ans==sum); } int main() { int l=inf,r=-inf,mid,ans=-1; n=read();m=read(); for(i=1;i<=m;i++){ e[i].u=read();e[i].v=read(); e[i].a=read();e[i].b=read(); if(e[i].a>e[i].b){ swap(e[i].u,e[i].v); swap(e[i].a,e[i].b); } l=min(l,e[i].a); r=max(r,e[i].b); } while(l<=r){ mid=(l+r)/2; if(check(mid)){ ans=mid; r=mid-1; } else l=mid+1; } if(ans==-1) puts("NIE"); else printf("%d\n",ans); return 0; }