luogu2472&&bzoj1066 [SCOI2007]蜥蜴

(http://www.elijahqi.win/2017/11/23/luogu2472bzoj1066-scoi2007%E8%9C%A5%E8%9C%B4/)
题目背景
07四川省选
题目描述
在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。
每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为1,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。
输入输出格式
输入格式:
输入第一行为三个整数r,c,d,即地图的规模与最大跳跃距离。以下r行为石竹的初始状态,0表示没有石柱,1~3表示石柱的初始高度。以下r行为蜥蜴位置,“L”表示蜥蜴,“.”表示没有蜥蜴。
输出格式:
输出仅一行,包含一个整数,即无法逃离的蜥蜴总数的最小值。
输入输出样例
输入样例#1: 复制
5 8 2
00000000
02000000
00321100
02000000
00000000
……..
……..
..LLLL..
……..
……..

输出样例#1: 复制
1
说明
100%的数据满足:1<=r, c<=20, 1<=d<=3
建边 每个柱子拆点 用来限制跳跃次数

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define inf 0x3f3f3f3f
using namespace std;
int h[1000],num=1;
int r,c,d,ans1,mp[22][22],level[1000];char s1[22];
struct node{
    int x,y,next,z;
}data[644000];
inline void insert1(int x,int y,int z){
    data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;data[num].x=x;
    data[++num].y=x;data[num].z=0;data[num].next=h[y];h[y]=num;data[num].x=y;
}
inline int calc(int i,int j,int op){return (i-1)*c*2+(j-1)*2+op;}
inline bool bfs(){
    memset(level,0,sizeof(level));queue<int>q;q.push(0);level[0]=1;
    while (!q.empty()){
        int x=q.front();q.pop();
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z;
            if (level[y]||!z) continue;q.push(y);
            level[y]=level[x]+1;if (y==900) return true;
        }
    }return false;
}
inline bool check(int x,int y,int x1,int y1){
    if (!mp[x1][y1]) return false;
    if ((x1-x)*(x1-x)+(y1-y)*(y1-y)>d*d) return false;else return true; 
}
int dfs(int x,int s){
    if (x==900) return s;int ss=s;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;
        if (level[x]+1==level[y]&&z){
            int xx=dfs(y,min(z,s));
            s-=xx;data[i].z-=xx;data[i^1].z+=xx;
            if (!s) return ss;
        }
    }return ss-s;
}
int main(){
    freopen("2472.in","r",stdin);
    scanf("%d%d%d",&r,&c,&d);
    for (int i=1;i<=r;++i){
        scanf("%s",s1+1);
        for (int j=1;j<=c;++j) mp[i][j]=s1[j]-'0';
    }
    for (int i=1;i<=r;++i){
        scanf("%s",s1+1);
        for (int j=1;j<=c;++j) if (s1[j]=='L') insert1(0,calc(i,j,1),1),ans1++;
    }
    for (int i=1;i<=r;++i){
        for (int j=1;j<=c;++j){
            if (!mp[i][j]) continue;
            for (int x=1;x<=r;++x){
                for (int y=1;y<=c;++y){
                    if (i==x&&j==y) continue;
                    if (check(i,j,x,y)){
                        insert1(calc(i,j,2),calc(x,y,1),inf);
                    }
                }
            }
            if (i<=d||abs(r-i+1)<=d||j<=d||abs(c-j+1)<=d)insert1(calc(i,j,2),900,inf);
        }
    }int ans=0;
    for (int i=1;i<=r;++i)
        for (int j=1;j<=c;++j)
            if (mp[i][j])insert1(calc(i,j,1),calc(i,j,2),mp[i][j]);
//  for (int i=2;i<=num;++i) printf("%d %d %d\n",data[i].x,data[i].y,data[i].z);
    while(bfs()) 
        ans+=dfs(0,inf);
    printf("%d",ans1-ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值