[最大流]BZOJ 1066——[SCOI2007]蜥蜴

1066: [SCOI2007]蜥蜴

题目描述

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

解题思路

比较裸的网络流,每个节点有高度可以把点拆掉新建容量为高度的边权,建超级源连接所有L节点,容量为1,建超级汇连接所有可以跳出边界的节点,其容量和互相到达的节点之间建的容量都为无限大,然后直接刷Dinic就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxv=805,maxm=1000005,MAXINT=2147483647;
int n,m,d,tot,lnk[maxv],que[maxv],sum,hed,til,dis[maxv],s,t,B[maxv];
int cap[maxm],flw[maxm],son[maxm],nxt[maxm];
bool vis[maxv];
inline char _read(){
    char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='.'&&ch!='L') ch=getchar();
    return ch;
}
int sqr(int x){return x*x;}
int getid(int i,int j,int k){return ((i-1)*m+j-1)*2+k;}
int getl(int x1,int y1,int x2,int y2){return sqr(x1-x2)+sqr(y1-y2);}
void add(int x,int y,int z){
    nxt[tot]=lnk[x];lnk[x]=tot;son[tot]=y;cap[tot]=z;tot++;
    nxt[tot]=lnk[y];lnk[y]=tot;son[tot]=x;cap[tot]=0;tot++;
}
void maker(){
    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    if (_read()=='L') add(s,getid(i,j,1),1),sum++;
    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    if (i<=d||j<=d||(n-i+1)<=d||(m-j+1)<=d) add(getid(i,j,2),t,maxv);
    for (int i1=1;i1<=n;i1++)
    for (int j1=1;j1<=m;j1++)
    for (int i2=1;i2<=n;i2++)
    for (int j2=1;j2<=m;j2++)
    if ((i1!=i2||j1!=j2)&&getl(i1,j1,i2,j2)<=sqr(d)) add(getid(i1,j1,2),getid(i2,j2,1),maxv);
}
bool BFS(){
    memset(vis,0,sizeof(vis));
    memset(dis,63,sizeof(dis));
    hed=0,til=1;que[1]=s;vis[s]=1;dis[s]=1;
    while (hed!=til){
        int x=que[++hed];
        for (int j=lnk[x];j!=-1;j=nxt[j])
        if (!vis[son[j]]&&cap[j]>flw[j]){
            dis[son[j]]=dis[x]+1;
            vis[son[j]]=1;que[++til]=son[j];
        }
    }
    return vis[t];
}
int DFS(int x,int mi){
    if (x==t||!mi) return mi;
    int num=0;
    for (int &j=B[x];j!=-1;j=nxt[j])
    if (dis[x]+1==dis[son[j]]){
        int now=DFS(son[j],min(mi,cap[j]-flw[j]));
        if (now){
            flw[j]+=now;flw[j^1]-=now;
            num+=now;mi-=now;
            if (!mi) break;
        }
    }
    return num;
}
void Dinic(){
    int ans=0;
    while (BFS()){
        memcpy(B,lnk,sizeof(B));
        ans+=DFS(s,MAXINT);
    }
    printf("%d\n",sum-ans);
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    memset(lnk,255,sizeof(lnk));
    scanf("%d%d%d",&n,&m,&d);
    s=0,t=2*n*m+1;
    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++) add(getid(i,j,1),getid(i,j,2),_read()-48);
    maker();
    Dinic();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值