marshland(最大费用可行流)

Description:

这里写图片描述

题解:

分成三类点:
(i+j)是奇数的,也就是需要去覆盖的点。
(i+j)是偶数,且i是奇数的点。
(i+j)是偶数,且i是偶数的点。

然后你就得到(i+j)是奇数的点一定是由相邻的(i+j)是偶数且i奇偶性不同的两个点覆盖,

然后就可以建图流了。

注意是最大费用可行流,用SPFA求最长路。

由于每次的增广路流量一定是1,所以只要找不超过m条增广路就行了。

Code:

#include<cstdio>
#define ll long long
#define min(a, b) ((a) < (b) ? (a) : (b))
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int N = 55, M = 1e6;

int move[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

int n, m, P, x, y, v[N][N], bx[N][N];
int num[N][N], td, S, T;
int final[M], to[M], next[M], r[M], tot = 1;
int d[M * 10], bz[M], la[M]; ll w[M], dis[M], ans, rans;

void link(int x, int y, int z, ll u) {
    next[++ tot] = final[x], to[tot] = y, r[tot] = z, w[tot] = u, final[x] = tot;
    next[++ tot] = final[y], to[tot] = x, r[tot] = 0, w[tot] = -u, final[y] = tot;
}

int spfa() {
    fo(i, 1, td) dis[i] = -1e18;
    dis[S] = 0; d[d[0] = 1] = S; bz[S] = 1;
    fo(i, 1, d[0]) {
        int x = d[i];
        for(int j = final[x]; j; j = next[j])
            if(r[j] && dis[x] + w[j] > dis[to[j]]) {
                dis[to[j]] = dis[x] + w[j]; la[to[j]] = j;
                if(!bz[to[j]]) bz[to[j]] = 1, d[++ d[0]] = to[j];
            }
        bz[x] = 0;
    }
    return dis[T] > 0;
}

void gg() {
    int x = T, minh = 1e9;
    while(x != S) minh = min(minh, r[la[x]]), x = to[la[x] ^ 1];
    x = T;
    while(x != S) ans -= minh * w[la[x]], r[la[x]] -= minh, r[la[x] ^ 1] += minh, x = to[la[x] ^ 1];
}

int main() {
    freopen("marshland.in", "r", stdin);
    freopen("marshland.out", "w", stdout);
    scanf("%d %d %d", &n, &m, &P);
    fo(i, 1, n) fo(j, 1, n) {
        scanf("%d", &v[i][j]);
        num[i][j] = ++ td;
    }
    fo(i, 1, P) {
        scanf("%d %d", &x, &y);
        bx[x][y] = 1;
    }
    S = ++ td; T = ++ td;
    fo(i, 1, n) fo(j, 1, n) {
        if(bx[i][j]) continue;
        if(i + j & 1) {
            link(num[i][j], ++ td, 1, v[i][j]); ans += v[i][j];
            fo(k, 0, 3) {
                int x = i + move[k][0], y = j + move[k][1];
                if(x && y && x <= n && y <= n && !bx[x][y]) {
                    if(x & 1) link(num[x][y], num[i][j], 1, 0); else link(td, num[x][y], 1, 0);
                 }
             }
        } else
        if(i & 1) {
            link(S, num[i][j], 1, 0);
        } else {
            link(num[i][j], T, 1, 0);
        }
    }
    while(rans < m && spfa()) gg(), rans ++;
    printf("%lld", ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值