bzoj1066: [SCOI2007]蜥蜴

题面在这里

题意:

一个n*m的网格中,有一些格子有石柱,石柱的高度为1~3.
有一些石柱的顶上有蜥蜴,蜥蜴每次可以跳到距离不超过d的石柱上,或者跳到界外。
蜥蜴跳一次,它所在的石柱的高度就减一,如果某个石柱的高度为0了,石柱就消失,以后蜥蜴不能跳到这里。
现在要使得剩下无法逃脱的蜥蜴数最少。

做法:

总是自己想不到建图方式然后看了题解顿时恍然大悟的感觉= =
好吧这题我还是想到了一点的恩。

题目就是要求逃脱的蜥蜴数最多。
然后就想到最大流了(什么鬼)
考虑把每个格子拆成两个点,表示石柱顶和石柱下端。
4种边:
1. 这个点有石柱,石柱顶连向石柱下端,容量为石柱高
2. 这个点有蜥蜴,源点连向石柱的上端,容量为1
3. 这个点可以跳出去,石柱的底连向汇点,容量inf
4. 两个点距离小于等于d,可以跳到,连边,容量inf
挺好理解的qaq

然后跑跑dinic你就a了orz。

代码:

/*************************************************************
    Problem: bzoj 1066 [SCOI2007]蜥蜴
    User: fengyuan
    Language: C++
    Result: Accepted
    Time: 24 ms
    Memory: 5992 kb
    Submit_Time: 2018-01-17 18:28:05
*************************************************************/

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<queue>
using namespace std;

const int N = 805, M = 400010;
const int inf = 1e9;
int n, m, d, cnt, all, s, t;
char str[30];
int head[N], vis[N];
struct edge{
    int to, nxt, c;
    edge() {}
    edge(int x, int y, int z) { to = x, nxt = y, c = z; }
}e[M];

inline int id1(int x, int y) { return (x-1)*m+y; }
inline int id2(int x, int y) { return (x-1)*m+y+n*m; }
inline int dis(int x1, int y1, int x2, int y2) { return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); }
inline void addedge(int x, int y, int z) {
    e[cnt] = edge(y, head[x], z); head[x] = cnt ++;
    e[cnt] = edge(x, head[y], 0); head[y] = cnt ++;
}
inline bool bfs()
{
    queue<int> q; q.push(s); memset(vis, -1, sizeof vis); vis[s] = 1;
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = e[i].nxt) {
            int v = e[i].to;
            if(e[i].c && vis[v] == -1) {
                vis[v] = vis[u]+1;
                q.push(v);
            }
        }
    }
    return vis[t] != -1;
}
inline int dfs(int u, int flow)
{
    if(u == t) return flow;
    int used = 0, w;
    for(int i = head[u]; i != -1; i = e[i].nxt) {
        int v = e[i].to;
        if(vis[v] == vis[u]+1 && e[i].c) {
            w = dfs(v, min(flow-used, e[i].c));
            e[i].c -= w; e[i^1].c += w; used += w;
            if(used == flow) return used;
        }
    }
    if(!used) vis[u] = -1;
    return used;
}
inline int dinic()
{
    int ret = 0;
    while(bfs()) ret += dfs(s, inf);
    return ret;
}
int main() {
    scanf("%d%d%d", &n, &m, &d); memset(head, -1, sizeof head);
    s = 0, t = n*m*2+1;//每个点拆成两个点,表示从石柱顶连向石柱下端
    for(int i = 1; i <= n; i ++) {
        scanf("%s", str+1);
        for(int j = 1; j <= m; j ++) if(str[j]-'0' > 0) addedge(id1(i, j), id2(i, j), str[j]-'0');
    }//这个点有石柱,石柱顶连向石柱下端,容量为石柱高 
    for(int i = 1; i <= n; i ++) {
        scanf("%s", str+1);
        for(int j = 1; j <= m; j ++) if(str[j] == 'L') addedge(s, id1(i, j), 1), all ++;
    }//这个点有蜥蜴,源点连向石柱的上端 
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            if(i-d < 1 || i+d > n || j-d < 1 || j+d > m) addedge(id2(i, j), t, inf);//可以跳出去,石柱的底连向汇点 
    for(int x1 = 1; x1 <= n; x1 ++)//距离小于等于d,可以跳到的两个点连边
        for(int y1 = 1; y1 <= m; y1 ++)
            for(int x2 = 1; x2 <= n; x2 ++)
                for(int y2 = 1; y2 <= m; y2 ++) if(dis(x1, y1, x2, y2) <= d*d) addedge(id2(x1, y1), id1(x2, y2), inf);
    printf("%d\n", all - dinic());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值