bzoj1391 最大权闭合子图(also最小割、网络流)

一道裸的最小割的题,写一下只是练练手。

表示被卡M,RE不开心。一道裸题至于吗?

再次复习一下最大权闭合子图:

1.每一个点若为正权,与源点连一条容量为绝对值权值的边。否则连向汇点一条容量为绝对值权值的边

2.如果有选了A点才能选B点的约束条件,且违背这个约束条件有C的代价,则从A点向B点连一条容量为C的边(如果不能违背,则连一条容量为INF的边)

3.设源点出度为C,最大流的值为D。答案为C-D。

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std ; 


namespace DINIC {

    const int MAXVn = 1200 * 2 + 20 ; 
    const int MAXEn = 1200 * 2 + 20 ;
    int Vn ; 

    struct edge {
        int p ; 
        int c ; 
        edge * nxt ; 
        edge * brother ; 
    } ;

    edge E [ MAXEn * 2 ] ; 
    namespace E_SET { edge * T = E ; } ;  
    edge * V [ MAXVn ] ; 

    int S , T ;

    void add_edge ( const int a , const int b , const int f ) {
        using E_SET :: T ; 
        T -> p = b ; T -> c = f ; T -> nxt = V [ a ] ; V [ a ] = T ++ ;
        T -> p = a ; T -> c = 0 ; T -> nxt = V [ b ] ; V [ b ] = T ++ ; 
        V [ a ] -> brother = V [ b ] ; V [ b ] -> brother = V [ a ] ; 
    }

    edge * cur [ MAXVn ] ;
    int dis [ MAXVn ] ; 

    bool bfs ( ) {
        queue < int > q ; 
        fill ( dis , dis + Vn , - 1 ) ;    
        copy ( V , V + Vn , cur ) ; 
        q . push ( S ) ; dis [ S ] = 0 ; 
        while ( ! q . empty () ) {
            const int o = q . front () ; q . pop () ; 
            for ( edge * v = V [ o ] ; v != 0 ; v = v -> nxt ) 
                if ( v -> c != 0 && dis [ v -> p ] == - 1 ) {
                    dis [ v -> p ] = dis [ o ] + 1 ; 
                    q . push ( v -> p ) ;
                }
        }
        return dis [ T ] != -1 ; 
    }

    int dfs ( const int o , int flow ) {
        if ( o == T || flow == 0 ) return flow ; 
        int f , ans = 0 ; 
        for ( edge * & v = cur [ o ] ; v != 0 ; v = v -> nxt ) 
        if ( dis [ o ] + 1 == dis [ v -> p ] && 
            ( f = dfs ( v -> p , min ( flow , v -> c ) ) ) != 0 ) {
            v -> c -= f ; v -> brother -> c += f ; 
            ans += f ; flow -= f ;
            if ( flow == 0 ) break ;
        }
        return ans ; 
    }

    int dinic ( ) {
        int ans = 0 ; 
        while ( bfs ( ) ) ans += dfs ( S , 1 << 30 ) ;
        return ans ; 
    }

}

int M , N ;
int sum ; 

int main () {

    using namespace DINIC ;  
    scanf ( "%d%d" , & N , & M ) ;
    Vn = M + N + 2 ; //in && out && S && T 
    S = 0 ; 
    T = 1 ; 

#define WORK(a) ((a)+2)
#define MACHINE(a) ((a)+N+2)

     for ( int i = 0 ; i < N ; ++ i ) {
        int value_of_w , num_of_w ; scanf ( "%d%d" , & value_of_w , & num_of_w ) ;
        sum += value_of_w ; 
        add_edge ( S , WORK(i) , value_of_w ) ;     
        while ( num_of_w -- ) {
            int n , pay ; 
            scanf ( "%d%d" , & n , & pay ) ;
            n -= 1 ; 
            add_edge ( WORK(i) , MACHINE(n) , pay ) ;
        }
    }
    for ( int i = 0 ; i < M ; ++ i ) {
        int pay ; scanf ( "%d" , & pay ) ;
        add_edge ( MACHINE(i) , T , pay ) ;
    }

#undef WORK 
#undef MACHINE

    printf ( "%d\n" , sum - dinic () ) ; 

    return 0 ; 

}

 

转载于:https://www.cnblogs.com/Christopher-Cao/p/5185634.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值