[BZOJ1937]-[Shoi2004]Mst最小生成树-KM算法

xiaoxiao2021-02-28  29

说在前面

感觉me变得颓废了… 闲得去看别人的NOI游记了… 以前总是觉得「和别人比高低、被催促着学习、不学就会落后」这样的感觉差极了 然而me现在才慢慢的感受到…竞争才能跑得更快


题目

BZOJ1937传送门 看题可戳传送门


解法

me记得me曾经用单纯形法水过了这道题emmmm

首先一个显然的事实:树边的权值只会减少,非树边的权值只会增加 然后我们相当于是要满足这个条件:任何一条非树边与这个树构成的环,非树边的权值最大

wi w i 为第 i i 条边的权值,didi 为改变量 然后就是要 widiwj+dj w i − d i ≤ w j + d j ,其中 i i 是树边,jj 是非树边。我们的目的是要最小化: dk ∑ d k 移项变成了: wiwjdi+dj w i − w j ≤ d i + d j 然后这就是一个线性规划的式子,直接单纯形暴力做就好了 然后我们发现,这个式子很像KM算法中对于顶标的定义,要求的东西也正好是KM算法的意义 所以直接用KM算法解决这道题

题外话:其实不难发现,最大权匹配也是线性规划的一种


下面是代码

#include <cstdio> #include <cstring> #include <algorithm> using namespace std ; int N , M , ide[55][55] , head[55] , tp ; struct Edge{ int u , v , w ; bool intree ; } e[855] ; struct Path{ int pre , to ; } p[1655] ; template < typename T > void smax( T &x , T y ){ if( x < y ) x = y ; } template < typename T > void smin( T &x , T y ){ if( x > y ) x = y ; } void In( int t1 , int t2 ){ p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ; p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ; } bool vy[805] , vx[805] ; int macy[805] , macx[805] , que[805] , fr , ba , lac[805] , pre[805] ; int dep[805] , fa[805] , w[805][805] , lx[805] , ly[805] ; void dfs( int u , int f ){ for( int i = head[u] ; i ; i = p[i].pre ){ int v = p[i].to ; if( v == f ) continue ; dep[v] = dep[u] + 1 ; fa[v] = u , dfs( v , u ) ; } } void Jump( int u , int v , int id ){ while( u != v ){ if( dep[u] < dep[v] ) swap( u , v ) ; int id2 = ide[ fa[u] ][u] ; w[id2][id] = e[id2].w - e[id].w ; u = fa[u] ; } } void preWork(){ dfs( 1 , 1 ) ; for( int i = 1 ; i <= M ; i ++ ) if( !e[i].intree ) Jump( e[i].u , e[i].v , i ) ; for( int i = 1 ; i <= M ; i ++ ) for( int j = 1 ; j <= M ; j ++ ) smax( lx[i] , w[i][j] ) ; } void match( int v ){ while( v ){ macy[v] = pre[v] ; swap( v , macx[ pre[v] ] ) ; } } void BFS( int St ){ fr = 1 , que[ ba = 1 ] = St , vx[St] = true ; while( true ){ while( ba >= fr ){ int u = que[fr++] , tmp ; for( int v = 1 ; v <= M ; v ++ ){ tmp = lx[u] + ly[v] - w[u][v] ; if( vy[v] || tmp >= lac[v] ) continue ; pre[v] = u ; if( !tmp ){ if( !macy[v] ) return match( v ) ; vy[v] = vx[ macy[v] ] = true ; que[++ba] = macy[v] ; } else lac[v] = tmp ; } } int rem = 0x3f3f3f3f , v ; for( int i = 1 ; i <= M ; i ++ ) if( !vy[i] && rem > lac[i] ) rem = lac[i] , v = i ; if( rem == 0x3f3f3f3f ) return ; for( int i = 1 ; i <= M ; i ++ ){ if( vx[i] ) lx[i] -= rem ; if( vy[i] ) ly[i] += rem ; else lac[i] -= rem ; } if( !macy[v] ) return match( v ) ; vy[v] = vx[ macy[v] ] = true , que[++ba] = macy[v] ; } } void solve(){ for( int i = 1 ; i <= M ; i ++ ) if( !macx[i] ){ memset( vx , 0 , sizeof( vx ) ) ; memset( vy , 0 , sizeof( vy ) ) ; memset( lac , 0x3f , sizeof( lac ) ) ; BFS( i ) ; } long long ans = 0 ; for( int i = 1 ; i <= M ; i ++ ) ans += lx[i] + ly[i] ; printf( "%lld\n" , ans ) ; } int main(){ scanf( "%d%d" , &N , &M ) ; for( int i = 1 , u , v , w ; i <= M ; i ++ ){ scanf( "%d%d%d" , &u , &v , &w ) ; e[i] = ( Edge ){ u , v , w , 0 } ; ide[u][v] = ide[v][u] = i ; } for( int i = 1 , u , v ; i < N ; i ++ ){ scanf( "%d%d" , &u , &v ) , In( u , v ) ; e[ ide[u][v] ].intree = true ; } preWork() ; solve() ; }
转载请注明原文地址: https://www.6miu.com/read-2629776.html

最新回复(0)