写这道题顺便学了学混合图欧拉回路判定,感觉自己萌萌哒!
话说me网络流连反向边都忘记建了,居然还可以过样例???
一直以来都十分佩服样例数据,以及造数据的人,无论程序有什么bug都能跑对…
BZOJ2095传送门
(直接概括题目大意就没什么意思了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
#include
#include
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() ;
}