BZOJ 1066 / POJ 2732 最大流

题意:在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。 每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1,如果该石柱原来高度为1,则蜥蜴离开后消失,以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。

每个柱子拆点限制容量,对每个点对从这个点能够到达的其他点暴力建图,源点连往所有初始有蜥蜴的点,设流量为1,对所有能跳到地图外的点连往汇点。
跑最大流即可。

#include <bits/stdc++.h>

using namespace std;

namespace dinic {
    static const int maxn = 1100;
    static const int maxm = 4e6+10;
    struct Edge{
        int u, v, next, flow, cap;
    }edge[maxm];

    int head[maxn], level[maxn], cur[maxn], eg;

    void addedge(int u, int v, int cap) {
        edge[eg] = {u, v, head[u], 0, cap}, head[u] = eg++;
        swap(u, v);
        edge[eg] = {u, v, head[u], 0, 0}, head[u] = eg++;
    }
    void init() {
        eg = 0;
        memset(head, -1, sizeof head);
    }
    bool makelevel(int s, int t, int n) {
        for(int i = 0; i < n; i++) {
            level[i] = 0; cur[i] = head[i];
        }
        queue<int> q; q.push(s);
        level[s] = 1;
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            for(int i = head[u]; ~i; i = edge[i].next) {
                Edge &e = edge[i];
                if(e.flow < e.cap && level[e.v] == 0) {
                    level[e.v] = level[u] + 1;
                    if(e.v == t) return 1;
                    q.push(e.v);
                }
            }
        }
        return 0;
    }
    int findpath(int s, int t, int limit = INT_MAX) {
        if(s == t || limit == 0) return limit;
        for(int i = cur[s]; ~i; i = edge[i].next) {
            cur[edge[i].u] = i;
            Edge & e = edge[i], &rev = edge[i^1];
            if(e.flow < e.cap && level[e.v] == level[s] + 1) {
                int flow = findpath(e.v, t, min(limit, e.cap - e.flow));
                if(flow > 0) {
                    e.flow += flow;
                    rev.flow -= flow;
                    return flow;
                }
            }
        }
        return 0;
    }
    int maxflow(int s, int t, int n) {
        int ans = 0;
        while(makelevel(s, t, n)) {
            int flow = 0;
            while((flow = findpath(s, t)) > 0) ans += flow;
        }
        return ans;
    }
}

namespace solver {
    int r, c, d;
    int deg[22][22];
    char g[22][22];
    bool i_can_jump(int x, int y) {
        if(x <= d || (r+1-x) <= d || y <= d || (c+1-y) <=d)
            return 1;
        return 0;
    }
    bool in(int x, int y, int px, int py) {
        if(abs(px-x) + abs(py-y) <= d)
            return 1;
        return 0;
    }
    void solve() {
        int s = 0, t = 1000;
        int gap = 500;
        dinic::init();
        scanf("%d%d%d", &r, &c, &d);
        int cnt = 0;
        for(int i = 1; i <= r; i++)
            for(int j = 1; j <= c; j++) {
                char k;
                scanf(" %c", &k);
                deg[i][j] = k-'0';
                if(i_can_jump(i, j) && deg[i][j])
                    dinic::addedge(gap + r*i+j, t, 0x3f3f3f3f);
                if(deg[i][j])
                    dinic::addedge(r*i+j, gap + r*i+j, deg[i][j]);
            }
        for(int i = 1; i <= r; i++)
            for(int j = 1; j <= c; j++) {
                scanf(" %c", &g[i][j]);
                if(g[i][j] == 'L' && deg[i][j])
                    dinic::addedge(s, r*i+j, 1), cnt++;
            }
        for(int i = 1; i <= r; i++)
            for(int j = 1; j <= c; j++) {
                for(int k = max(1, i-d); k <= min(r, i+d); k++) {
                    for(int p = max(1, j-d); p <= min(c, j+d); p++) {
                        if(in(i, j, k, p) && !(i==k&&j==p) && deg[i][j] && deg[k][p]) {
                            dinic::addedge(i*r+j+gap, k*r+p, 0x3f3f3f3f);
                        }
                    }
                }
            }
        printf("%d\n", cnt - dinic::maxflow(s, t, t+1));
    }
}

int main() {
    solver::solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值