1. 程式人生 > >[BZOJ2095]-[Poi2010]Bridges-二分答案+混合圖歐拉回路判定

[BZOJ2095]-[Poi2010]Bridges-二分答案+混合圖歐拉回路判定

說在前面

寫這道題順便學了學混合圖歐拉回路判定,感覺自己萌萌噠!
話說me網路流連反向邊都忘記建了,居然還可以過樣例???
一直以來都十分佩服樣例資料,以及造資料的人,無論程式有什麼bug都能跑對…

題目

題面

(直接概括題目大意就沒什麼意思了hhhh)
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座橋連線小島a和b,從a到b承受的風力為c,從b到a承受的風力為d

輸出格式:
如果無法完成減肥計劃,則輸出NIE,否則第一行輸出最大承受風力的最小值

解法

題目就是要我們找一條歐拉回路(每個橋經過一次就好,不管方向),使得這條迴路上權值最大的儘量小
二分答案是顯然的,關鍵是如何check

每次二分一個mid,大於mid的邊都不選,那麼就有一些方向不能走了,原圖就是一個混合圖,問題就轉化成了一個混合圖判定歐拉回路問題(如果有一條邊兩個方向都不能走,那肯定不存在歐拉回路)
對於那些單向邊,直接統計度數就可以。對於兩個方向都可以走的邊,先隨便定一個方向,假設是u->v,統計度數,並且在網路圖裡加一條邊u->v,流量為1。
最後遍歷所有的點u,如果入度與出度之差為奇數,顯然無解(某一次進去就出不來了,或者出來就進不去了)
如果u出度大於入度,那麼加邊S->u,流量(out-in)/2
如果u入度大於出度,那麼加邊u->T,流量(in-out)/2
最後滿流才有歐拉回路

這樣做的正確性:
雙向邊是隨意定向的,因此這個定向可能是錯的,才會導致一些點出度和入度不相等(如果歐拉回路本來存在的話)。如果把一條原本是v->u的邊定成了u->v,那麼u就多了出度,v就少了入度。要修正這個錯誤,就需要讓u->v邊反向,達到u出度-1,v入度+1的目的。也就是S->u->v->T這條流量的含義。當然反過來建也是可以的:S->v->u->T
如果最後不能滿流,說明有一些點的入度沒辦法等於出度,那肯定不存在歐拉回路

成功get新姿勢!

下面是自帶大常數的程式碼

#include <cstdio>
#include <cstring> #include <algorithm> using namespace std ; int N , M , ind[1005] , otd[1005] , head[1005] , tp , S = 0 , T = 1001 ; struct Edges{ int u , v , Uv , Vu ; }e[2005] ; struct Path{ int pre , to , flow ; }p[100005] ; void In( int t1 , int t2 , int t3 ){ p[++tp].pre = head[t1] ; p[ head[t1] = tp ].to = t2 ; p[tp].flow = t3 ; } int dis[1005] , que[1005] , fr , ba ; bool Bfs(){ fr = 1 , ba = 0 ; memset( dis , -1 , sizeof( dis ) ) ; dis[S] = 0 ; que[++ba] = S ; while( fr <= ba ){ int u = que[fr++] ; for( int i = head[u] ; i ; i = p[i].pre ){ int v = p[i].to ; if( !p[i].flow || dis[v] != -1 ) continue ; dis[v] = dis[u] + 1 ; que[++ba] = v ; } } return dis[T] != -1 ; } int dfs( int u , int flow ){ if( u == T || !flow ) return flow ; int totf = 0 ; for( int i = head[u] ; i ; i = p[i].pre ){ int v = p[i].to ; if( dis[v] != dis[u] + 1 || !p[i].flow ) continue ; int nowf = dfs( v , min( flow , p[i].flow ) ) ; if( nowf ){ flow -= nowf ; totf += nowf ; p[i].flow -= nowf ; p[i^1].flow += nowf ; } } if( !totf ) dis[u] = -1 ; return totf ; } bool check( int mid ){ tp = 1 ; memset( ind , 0 , sizeof( ind ) ) ; memset( otd , 0 , sizeof( otd ) ) ; memset( head , 0 , sizeof( head ) ) ; int ans = 0 , sum = 0 ; for( int i = 1 ; i <= M ; i ++ ){ int u = e[i].u , v = e[i].v ; if( e[i].Uv <= mid && e[i].Vu <= mid ){ In( u , v , 1 ) ; In( v , u , 0 ) ; otd[u] ++ , ind[v] ++ ; } else if( e[i].Uv <= mid ) otd[u] ++ , ind[v] ++ ; else if( e[i].Vu <= mid ) otd[v] ++ , ind[u] ++ ; else return false ; } for( int i = 1 ; i <= N ; i ++ ){ int delta = otd[i] - ind[i] ; if( delta&1 ) return false ; if( delta > 0 ){ In( S , i , delta / 2 ) ; In( i , S , 0 ) ; sum += delta/2 ; } if( delta < 0 ){ In( i , T , -delta / 2 ) ; In( T , i , 0 ) ; } } while( Bfs() ) ans += dfs( S , 0x3f3f3f3f ) ; return ans == sum ; } void solve(){ int lf = 1 , rg = 1000 , ans = -1 ; while( lf <= rg ){ int mid = ( lf + rg ) >> 1 ; if( check( mid ) ){ ans = mid ; rg = mid - 1 ; } else lf = mid + 1 ; } if( ans == -1 ) puts( "NIE" ) ; else printf( "%d" , ans ) ; } int main(){ scanf( "%d%d" , &N , &M ) ; for( int i = 1 ; i <= M ; i ++ ) scanf( "%d%d%d%d" , &e[i].u , &e[i].v , &e[i].Uv , &e[i].Vu ) ; solve() ; }