BZOJ1066 ||洛谷P2472 [SCOI2007]蜥蜴【最大流】

Time Limit: 1 Sec
Memory Limit: 162 MB

Description

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

Input

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

Output

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

HINT

100%的数据满足:1<=r, c<=20, 1<=d<=4


题目分析

首先可以把问题转化为蜥蜴总数-最大逃离数,然后开始建图
每个石柱拆成入点和出点
并从入点向出点连边容量为自己的高度

对于每个可以直接跳出边界的石柱
从其出点向超级汇点连边,容量为inf

对于每个有蜥蜴的石柱
由超级源点向其入点连边,容量为1

对于原图中互相可达的石柱
从一个石柱的出点向另一个石柱的入点连边,容量为inf

跑最大流即可


#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
 
int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
 
const int inf=1e8;
const int maxn=10010;
int n,m,d,s=0,t,sum;
struct node{int v,f,nxt;}E[maxn*10];
int head[maxn],tot=1;
int judge[maxn],lev[maxn],ans;
char ss[25];
 
void add(int u,int v,int f)
{
    E[++tot].nxt=head[u];
    E[tot].v=v; E[tot].f=f;
    head[u]=tot;
}
 
int dis(int x1,int y1,int x2,int y2)
{
    int x=x1-x2; x*=x;
    int y=y1-y2; y*=y;
    return x+y;
}
 
bool bfs()
{
    queue<int> q; q.push(s);
    memset(lev,-1,sizeof(lev)); lev[s]=0;
 
    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(lev[v]==-1&&E[i].f)
            {
                lev[v]=lev[u]+1;
                if(v==t)return true;
                q.push(v);
            }
        }
    }
    return false;
}
 
int dfs(int u,int cap)
{
    if(u==t) return cap;
    int flow=cap;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if( lev[v]==lev[u]+1&&flow&&E[i].f>0)
        {
            int f=dfs(v,min(flow,E[i].f));
            E[i].f-=f; E[i^1].f+=f; 
            flow-=f;
        }
    }
    return cap-flow;
}
 
int main()
{
    n=read();m=read();d=read();
    t=n*m<<1|1;
    for(int i=1;i<=n;++i)
    {
        scanf("%s",&ss);
        for(int j=0;j<m;++j)
        {
            int num1=(i-1)*m+j+1,num2=num1+n*m,k=ss[j]-'0';
            if(k==0){ judge[num1]=1; continue;}//记录这个点没有石柱
            add(num1,num2,k); add(num2,num1,0);//每个点由自己的入点向自己的出点连边
            if(i-d<1||i+d>n||j-d+1<1||j+d+1>m) add(num2,t,inf),add(t,num2,0);//可以直接跳出去的点,从出点到超汇连边
        }
    }
 
    for(int i=1;i<=n;++i)
    {
        scanf("%s",&ss);
        for(int j=0;j<m;++j)
        {
            int num=(i-1)*m+j+1;
            if(ss[j]=='L'){
                sum++;
                add(s,num,1); add(num,s,0);//有蜥蜴的石柱,超源向其入点连边
            }
        }
    }
     
    for(int x1=1;x1<=n;++x1)
    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)
    {
        int num1=(x1-1)*m+y1+n*m,num2=(x2-1)*m+y2;
        if(judge[num2]) continue;
        add(num1,num2,inf); add(num2,num1,0);
        //互相可达的石柱,从其中一个的出点向另一个的入点连边
    }
     
    while(bfs())
    ans+=dfs(s,inf);
    printf("%d",sum-ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值