BZOJ-3894 文理分科 网络流建图 最小割 Dinic

大家都很强, 可与之共勉。

3894: 文理分科
Description
文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠
结过)
小P所在的班级要进行文理分科。他的班级可以用一个n*m的矩阵进行
描述,每个格子代表一个同学的座位。每位同学必须从文科和理科中选择
一科。同学们在选择科目的时候会获得一个满意值。满意值按如下的方式
得到:
1.如果第i行第秒J的同学选择了文科,则他将获得art[i][j]的满意值,如
果选择理科,将得到science[i][j]的满意值。
2.如果第i行第J列的同学选择了文科,并且他相邻(两个格子相邻当且
仅当它们拥有一条相同的边)的同学全部选择了文科,则他会更开
心,所以会增加same_art[i][j]的满意值。
3.如果第i行第j列的同学选择了理科,并且他相邻的同学全部选择了理
科,则增加same_science[i]j[]的满意值。
小P想知道,大家应该如何选择,才能使所有人的满意值之和最大。请
告诉他这个最大值。

Input
第一行为两个正整数:n,m
接下来n术m个整数,表示art[i][j];
接下来n术m个整数.表示science[i][j];
接下来n术m个整数,表示same_art[i][j];

Output
输出为一个整数,表示最大的满意值之和

Sample Input
3 4
13 2 4 13
7 13 8 12
18 17 0 5

8 13 15 4
11 3 8 11
11 18 6 5

1 2 3 4
4 2 3 2
3 1 0 4

3 2 3 2
0 2 2 1
0 2 4 4
Sample Output
152
HINT

个人觉得他的输入描述很不堪。。。
令S集为学文,T集为学理, 每个人学文或者学理的满意度很好连边,
如果某个集合内的人都学理会获得一个满意度,那么就新加一个点,将集合内的所有人向这个点连流量为正无穷的边, 再从这个点向T连一条流量为满意度的边, 表示集合内任意一个人学文都要把这个点与T的边割掉。
都学文同理

盗一张ZWD的图

建完图之后跑最小割即可

/************************************************************** 
    Problem: 3894 
    User: Lazer2001 
    Language: C++ 
    Result: Accepted 
    Time:1996 ms 
    Memory:13012 kb 
****************************************************************/

#include <cstdio> 
#include <cstring> 

#define Inf 0x3f3f3f3f 
#define max( a, b )  ((a) > (b) ? (a) : (b)) 
#define min( a, b )  ((a) < (b) ? (a) : (b)) 

typedef class EdgeData  { 
public: 
    int to, w, nxt ; 
     EdgeData ( ) { } 
     EdgeData ( int to, int w, int nxt ) : to ( to ), w ( w ), nxt ( nxt ) {    }    
} Edge ; 

Edge G[1000005] ; 
int ne = 1, head[30005] ; 

int n, m, x, ans; 

const int S = 0, T = 30004; 
const int dx[] = { 0, 0, 0, 1, -1 } ;   
const int dy[] = { 0, 1, -1, 0, 0 } ;   

#define P(x, y)  ((x - 1) * (m) + (y)) 

#define Adde( u, v, w ) ;  {  G[++ ne] = Edge ( v, w, head[u] ) ;  head[u] = ne ;   } 

inline void AddDbe ( int u, int v, int w )  { 
    Adde ( u, v, w ) ;  Adde ( v, u, 0 ) ; 
} 

class Network_Flows  { 
private: 
    int S, T, ret ; 
    int cur[30005], dep[30005] ; 

    inline short Bfs ( )  { 
        static int fr, tl, q[30005] ; 
        static int u, v; 
        memset ( dep, 0, sizeof ( int ) * ( T + 1 ) ) ; 
        fr = tl = 0 ; 
        q[++ tl] = S ; 
        dep[S] = 1 ; 
        while ( fr ^ tl )  { 
            u = q[++ fr] ; 
            for ( int i = head[u] ; i ; i = G[i].nxt )  { 
                v = G[i].to; 
                if ( G[i].w && !dep[v] )  { 
                    dep[v] = dep[u] + 1 ; 
                    q[++ tl] = v ; 
                } 
            } 
        } 
        return dep[T] != 0 ; 
    } 

    int Dfs ( int u, int a )  { 
        if ( u == T || !a )  return a ; 
        int v, f, flow = 0 ; 
        for( int& i = cur[u] ; i ; i = G[i].nxt )  { 
            v = G[i].to ; 
            if ( dep[v] == dep[u] + 1 )  { 
                f = Dfs ( v, min( a - flow, G[i].w ) ) ; 
                G[i].w -= f ;   G[i ^ 1].w += f ; 
                flow += f ; 
                if ( flow == a )  return flow ; 
            } 
        } 
        if ( !flow )  dep[u] = -1 ; // cannot be 0; 
        return flow; 
    } 

public: 
    inline int Dinic ( int S, int T )  { 
        this -> S = S, this -> T = T ; 
        ret = 0 ; 
        while ( Bfs ( ) )  { 
            memcpy ( cur, head, sizeof ( int ) * ( T + 1 ) ) ; 
            ret += Dfs ( S, Inf ) ; 
        } 
        return ret ; 
    } 
} Lazer ; 

int main ( )  { 
    scanf ( "%d%d", &n, &m ) ; 
    for ( int i = 1 ; i <= n ; ++ i ) 
        for ( int j = 1 ; j <= m ; ++ j )  { 
            scanf ( "%d", &x ) ; 
            ans += x; 
            AddDbe ( S, P(i, j), x ) ; 
        } 

    for ( int i = 1 ; i <= n ; ++ i ) 
        for ( int j = 1 ; j <= m ; ++ j )  { 
            scanf ( "%d", &x ) ; 
            ans += x; 
            AddDbe ( P(i, j), T, x ) ; 
        } 

    for ( int i = 1 ; i <= n ; ++ i ) 
        for ( int j = 1 ; j <= m ; ++ j )  { 
            scanf ( "%d", &x ) ;   
            ans += x ;   
            AddDbe ( S, P(i, j) + m * n, x ) ;   
            for( int k = 0 ; k <= 4 ; ++ k )  {   
                int x = i + dx[k] ;   
                int y = j + dy[k] ;   
                if ( x <= 0 || y <= 0 || x > n || y > m )  continue ;   
                AddDbe (P(i, j) + m * n, P(x, y) , Inf ) ;   
            }   
        }   

    for ( int i = 1 ; i <= n ; ++ i ) 
        for ( int j = 1 ; j <= m ; ++ j )  {  
            scanf ( "%d", &x ) ;   
            ans += x ;  
            AddDbe ( P(i, j) + ( ( m * n ) << 1 ), T, x ) ;   
            for( int k = 0 ; k <= 4 ; ++ k )  {  
                int x = i + dx[k] ;   
                int y = j + dy[k] ;    
                if ( x <= 0 || y <= 0 || x > n || y > m )  continue ;   
                AddDbe ( P(x, y) , P(i, j) + ( ( m * n ) << 1 ) , Inf ) ;   
            }   
        }   
    printf ( "%d\n", ans - Lazer.Dinic ( S, T ) ) ; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值