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

说在前面

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


题目

BZOJ1937传送门
看题可戳传送门


解法

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

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

w i 为第 i 条边的权值, d i 为改变量
然后就是要 w i d i w j + d j ,其中 i 是树边, j 是非树边。我们的目的是要最小化: d k
移项变成了: 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() ;
}

猜你喜欢

转载自blog.csdn.net/izumi_hanako/article/details/80362442
今日推荐