[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() ;
}