题解 P2472 【[SCOI2007]蜥蜴】

P2472 [SCOI2007]蜥蜴

题目背景
07四川省选

题目描述
在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。

每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为1,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。

输入输出格式
输入格式:
输入第一行为三个整数r,c,d,即地图的规模与最大跳跃距离。以下r行为石竹的初始状态,0表示没有石柱,1~3表示石柱的初始高度。以下r行为蜥蜴位置,“L”表示蜥蜴,“.”表示没有蜥蜴。

输出格式:
输出仅一行,包含一个整数,即无法逃离的蜥蜴总数的最小值。


解决这题的关键是理解这句话:石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为1,则蜥蜴离开后消失

我们把这句话转换一下,每个石柱 (i) 可以用石柱高度 (hi)。这是一个限制条件:使用次数,所以我们想到了最大流。

不能逃脱的蜥蜴数即为总蜥蜴数 - 逃脱的蜥蜴数,所以我们现在思考怎么建图

建模

最大流的核心在于限制

由限制条件,对网络流模型极其敏感我们可知道:石柱一定是要拆成两个点的,两点之间边容量为石柱高度 (h) ,这样可以限制一个石柱只能跳 (h)

由最大流答案不可能大于蜥蜴总数,所以我们可以联想到由汇点向蜥蜴所在的石柱连边,容量为 1

由石柱之间在规定距离内可以相互到达,傻子都会我们可以在距离内的石柱连一条容量为INF的边

由要跳出去可得:在跳到外围距离小于d的石柱连一条容量为INF的边

注意:因为每次都要消耗石柱高度,所以连入石柱的边连 石柱1 ,连出石柱的边从 石柱2 出发,从而达到路过石柱消耗高度这一限制

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 100019,INF = 1e9;
int num,nume = 1,tot;
int lenx,leny,d;
int map[190][190];
int L[190][190];
int s,t,maxflow;
int head[maxn];
struct Node{
    int v,dis,nxt;
    }E[maxn << 2];
void add(int u,int v,int dis){
    E[++nume].nxt = head[u];
    E[nume].v = v;
    E[nume].dis = dis;
    head[u] = nume;
    }
int lev[maxn];
bool bfs(){
    queue<int>Q;
    memset(lev,0,sizeof(lev));
    Q.push(s);
    lev[s] = 1;
    while(!Q.empty()){
        int u = Q.front();
        Q.pop();
        for(int i = head[u];i;i = E[i].nxt){
            int v = E[i].v;
            if(E[i].dis && !lev[v]){
                lev[v] = lev[u] + 1;
                Q.push(v);
                if(v == t)return 1;
                }
            }
        }
    return 0;
    }
int Dinic(int u,int flow){
    if(u == t)return flow;
    int rest = flow,k;
    for(int i = head[u];i;i = E[i].nxt){
        int v = E[i].v;
        if(E[i].dis && lev[v] == lev[u] + 1 && rest){
            k = Dinic(v,min(rest,E[i].dis));
            if(!k)lev[v] = 0;
            E[i].dis -= k;
            E[i ^ 1].dis += k;
            rest -= k;
            }
        
        }
    return flow - rest;
    }
double getdis(int x1,int y1,int x2,int y2){
    return ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }
int getindex(int x,int y){
    return (x - 1) * leny + y;
    }
int main(){
    lenx = RD();leny = RD();d = RD();
    char temp;
    for(int i = 1;i <= lenx;i++){
        for(int j = 1;j <= leny;j++){
            cin>>temp;
            map[i][j] = temp - '0';
            }
        }
    for(int i = 1;i <= lenx;i++){
        for(int j = 1;j <= leny;j++){
            cin>>temp;
            if(temp == 'L'){
                L[i][j] = 1;
                num++;
                }
            }
        }
    //石头1部从1到tot(lenx * leny),2部 + tot,石头编号为(i - 1) * leny + j
    tot = lenx * leny;
    s = tot * 2 + 1,t = s + 1;
    for(int i = 1;i <= lenx;i++){
        for(int j = 1;j <= leny;j++){
            int I = getindex(i,j);
            if(map[i][j]){
                add(I,I + tot,map[i][j]);
                add(I + tot,I,0);
                if(i + d > lenx || i - d < 1 || j + d > leny || j - d < 1){
                    add(I + tot,t,INF);
                    add(t,I + tot,INF);
                    }
                if(L[i][j]){
                    add(s,I,1);
                    add(I,s,0);
                    }
                }
            }
        }
    for(int x1 = 1;x1 <= lenx;x1++){
        for(int y1 = 1;y1 <= leny;y1++){
            for(int x2 = 1;x2 <= lenx;x2++){
                for(int y2 = 1;y2 <= leny;y2++){
                    if(getdis(x1,y1,x2,y2) <= d * d){
                        int u = getindex(x1,y1),v = getindex(x2,y2);
                        add(u + tot,v,INF);
                        add(v,u + tot,0);
                        add(v + tot,u,INF);
                        add(u,v + tot,0);
                        }
                    }
                }
            }
        }
    int flow = 0;
    while(bfs())while(flow = Dinic(s,INF))maxflow += flow;
    printf("%d
",num - maxflow);
    return 0;
    }
原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9285519.html