【 bzoj 2132 】GDOI圈地计划 - 最小割

5 篇文章 0 订阅
5 篇文章 0 订阅

  考虑黑白染色成二分图。我们要的是最大收益,那么就应该是删掉最小的一些收益。对于白点 W ,连SW WT ,容量分别为A收入和B收入,黑点则反过来,然后有关系的双向连上容量为C的和的边。
  这样割掉之后就行了。

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i = a , _ = b ; i <= _ ; i ++)
#define per(i,a,b) for (int i = a , _ = b ; i >= _ ; i --)
#define fore(i,u)  for (int i = head[u] ; i ; i = nxt[i])

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

const int inf = 0x7fffffff;
const int maxn = 10007;
const int maxm = 160007;

const int fx[] = {0 , 0 , 1 , -1};
const int fy[] = {1 , -1 , 0 , 0};

typedef int arr[maxn];
typedef int adj[maxm];
typedef int mat[107][107];

arr head , dis , cur;
adj to , nxt , cap , flow;
mat a , b , c;

queue<int> Q;

int n , m , ett , ans , S ,T;

inline int id(int x , int y) { return (x - 1) * m + y ; }

void input() {
    n = rd() , m = rd() , ett = 1;
    memset(c , -1 , sizeof c);
    rep (i , 1 , n) rep (j , 1 , m) a[i][j] = rd();
    rep (i , 1 , n) rep (j , 1 , m) b[i][j] = rd();
    rep (i , 1 , n) rep (j , 1 , m) c[i][j] = rd();
}

inline void ins(int u , int v , int c) {
//  printf("%d %d %d\n" , u , v , c);
    to[++ ett] = v , nxt[ett] = head[u] , cap[ett] = c , head[u] = ett;
    to[++ ett] = u , nxt[ett] = head[v] , cap[ett] = 0 , head[v] = ett;
}

inline bool bfs() {
    rep (i , 1 , T) dis[i] = 0;
    dis[S] = 1 , Q.push(S);
    while (!Q.empty()) {
        int u = Q.front() ; Q.pop();
        fore (i , u) if (cap[i] > flow[i]) {
            int v = to[i];
            if (!dis[v]) {
                dis[v] = dis[u] + 1;
                Q.push(v);
            }
        }
    }
    return dis[T] > 0;
}

int dfs(int u , int a) {
    if (u == T || !a) return a;
    int ret = 0 , f;
    for (int&i = cur[u] ; i ; i = nxt[i]) {
        int v = to[i];
        if (dis[v] == dis[u] + 1 && (f = dfs(v , min(a , cap[i] - flow[i])))) {
            flow[i] += f , flow[i ^ 1] -= f , a -= f , ret += f;
            if (!a) break;
        }
    }
    return ret;
}

int max_flow() {
    int ret = 0;
    while (bfs()) {
        rep (i , 1 , T) cur[i] = head[i];
        ret += dfs(S , inf);
    }
    return ret;
}

void solve() {
    S = n * m + 1 , T = n * m + 2;
    rep (i , 1 , n) rep (j , 1 , m) {
        if (i + j & 1) ins(S , id(i , j) , a[i][j]) , ins(id(i , j) , T , b[i][j]);
        else ins(S , id(i , j) , b[i][j]) , ins(id(i , j) , T , a[i][j]);
        ans += a[i][j] + b[i][j];
        if (!(i + j & 1)) continue;
        rep (d , 0 , 3) {
            int x = i + fx[d] , y = j + fy[d];
            if (c[x][y] == -1) continue;
            ins(id(i , j) , id(x , y) , c[i][j] + c[x][y]);
            ins(id(x , y) , id(i , j) , c[i][j] + c[x][y]);
            ans += c[i][j] + c[x][y];
        }
    }
//  printf("%d\n" , ans);   
    ans -= max_flow();
    printf("%d\n" , ans);
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.txt" , "r" , stdin);
    #endif
    input();
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值