[USACO]奶牛电信

题目链接 : 洛谷1345

题目大意
给出一个 N 个点 M 条边的无向图,与 S,T , 问至少删去多少个点,使 S,T 不连通。( N100,M600 )

分析
1. 对于这种分离 S,T 的题,考虑最大流–最小割的方法。
2. 然而题目要求割点,而不是割边,怎么办?这就是一种经典的拆点题。
3. 把每个点 i 拆成两个点i,i+n 且连一条容量为1的有向边(前者向后者连,下同); 若 i,j 之间有边,则在 i+n,j 之间和 j+n,i 之间连容量为 inf 的有向边。
4. 为什么要这样连边?这样可以保证最小割只割点,不割边。而且只能 i+nj 连不能 ji+n 连,后者会增大最大流。
5. 接下来就可以直接跑最大流–最小割了。

上代码

// 主要思路已在分析中给出,代码中不作多余阐述
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std ;

const int N = 300 + 5 ;
const int M = 3e3 + 5 ;
const int INF = 0x3f3f3f3f ;

int n, m, S, T ;

int len, head[ N ] ;
struct node_lib {
    int to, val, next ;
    inline void add( int a , int b , int c ) {
        to = b, val = c, next = head[ a ] ;
        head[ a ] = len ++ ;
    }
} lib[ M << 1 ] ;

// 建图
inline void init() {
    memset( head , 0xff , sizeof( head ) ) ;
    scanf( "%d %d %d %d", &n ,&m, &S, &T ) ;
    for ( int i = 1 ; i <= n ; i ++ )
        if ( i == S || i == T ) {
            lib[ len ].add( i , i + n , INF ) ;
            lib[ len ].add( i + n , i , 0 ) ;
        } else {
            lib[ len ].add( i , i + n , 1 ) ;
            lib[ len ].add( i + n , i , 0 ) ;
        }
    for ( int i = 1 ; i <= m ; i ++ ) {
        int a, b ; scanf( "%d %d", &a, &b ) ;
        lib[ len ].add( a + n , b , INF ) ;
        lib[ len ].add( b , a + n , 0 ) ;
        lib[ len ].add( b + n , a , INF ) ;
        lib[ len ].add( a , b + n , 0 ) ;
    }
}

queue < int > Q ;
int curn[ N ], step[ N ] ;
inline bool bfs() {
    memset( step , 0 , sizeof( step ) ) ;
    step[ S ] = 1 ; Q.push( S ) ;
    while ( !Q.empty() ) {
        int temp = Q.front() ; Q.pop() ;
        for ( int p = head[ temp ] ; p != -1 ; p = lib[ p ].next ) {
            int nown = lib[ p ].to ;
            if ( !step[ nown ] && lib[ p ].val ) {
                step[ nown ] = step[ temp ] + 1 ; Q.push( nown ) ;
            }
        }
    }
    if ( step[ T ] )    return true ;
    else    return false ;
}
int dinic( int a , int b ) {
    if ( a == T )   return b ;
    for ( int p = curn[ a ] ; p != -1 ; p = lib[ p ].next ) {
        curn[ a ] = p ;
        int nown = lib[ p ].to ;
        if ( step[ nown ] == step[ a ] + 1 && lib[ p ].val ) {
            int temp = dinic( nown , min( b , lib[ p ].val ) ) ;
            if ( temp ) {
                lib[ p ].val -= temp ;
                lib[ p ^ 1 ].val += temp ;
                return temp ;
            }
        }
    }
    return 0 ;
}
inline int figure() {
    int ans = 0 ;
    while ( bfs() ) {
        for ( int i = 1 ; i <= n * 2 ; i ++ )
            curn[ i ] = head[ i ] ;
        while ( int temp = dinic( S , INF ) )
            ans += temp ;
    }
    return ans ;
}
int main() {
    init() ;
    printf( "%d\n", figure() ) ;
    return 0 ;
}

以上

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值