POI2010 Mos-Bridges(二分答案+歐拉回路+網路流)
阿新 • • 發佈:2018-11-11
【題目描述】
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; }