蜥蜴[SCOI2007,BZOJ 1066]

33 篇文章 0 订阅
24 篇文章 0 订阅

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=1066


蜥蜴


Description

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


Input

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


Sample Input

5 8 2
00000000
02000000
00321100
02000000
00000000
……..
……..
..LLLL..
……..
……..


Sample Output

1


HINT

友情提示:题干中的距离指的是曼哈顿距离(我因为这个WA了一次)。


Solution

这道题我的思路是这样的:
因为对于任意一个有高度的石柱,只能有不超过一定数量的蜥蜴跳到它的上面,所以我们可以把这个“跳跃”的动作看作一个“流”,而所有流的流量之和不能超过这个石柱的高度。
所以我们可以设置一个虚点:将“跳到它之上”的流全部汇集到这个虚点上,再从虚点流到这个石柱上,设置流量的限制。
这样就 OK 了。


Code

[cpp]
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <queue>  
  5.   
  6. #define Min(a,b) ((a)<(b)?(a):(b))  
  7. #define abs(a) ((a)>0?(a):(-(a)))  
  8.   
  9. using namespace std;  
  10.   
  11. char s1[100][100];  
  12. char s2[100][100];  
  13.   
  14. int r,c,d,s,t,cnt,tot;  
  15.   
  16. const int INF=0x3f3f3f3f;  
  17. const int END=20*20+20*20+21*21;  
  18. const int START=20*20+20*20+20*20;  
  19. const int MAXN=100000;  
  20.   
  21. int data[MAXN*2],low[MAXN*2];  
  22. int nxt[MAXN*2],head[MAXN];  
  23. int dis[MAXN];  
  24. queue<int>q;  
  25.   
  26. void add(int x,int y,int z){  
  27.     nxt[cnt]=head[x];data[cnt]=y;low[cnt]=z;head[x]=cnt++;  
  28.     nxt[cnt]=head[y];data[cnt]=x;low[cnt]=0;head[y]=cnt++;  
  29. }  
  30.   
  31. bool BFS(){  
  32.     memset(dis,-1,sizeof dis);  
  33.     dis[s]=0;  
  34.     q.push(s);  
  35.     while(!q.empty()){  
  36.         int now=q.front();  
  37.         q.pop();  
  38.         for(int i=head[now];i!=-1;i=nxt[i])  
  39.             if(low[i]&&dis[data[i]]<0){  
  40.                 dis[data[i]]=dis[now]+1;  
  41.                 q.push(data[i]);  
  42.             }  
  43.     }  
  44.     return dis[t]>0;  
  45. }  
  46.   
  47. int dfs(int now,int flow){  
  48.     if(now==t)return flow;  
  49.     int Low;  
  50.     for(int i=head[now];i!=-1;i=nxt[i]){  
  51.         if(low[i]&&dis[data[i]]==dis[now]+1){  
  52.             if(Low=dfs(data[i],Min(flow,low[i]))){  
  53.                 low[i]-=Low;  
  54.                 low[i^1]+=Low;  
  55.                 return Low;  
  56.             }  
  57.         }  
  58.     }  
  59.     return 0;  
  60. }  
  61.   
  62. int main(){  
  63.       
  64.     memset(head,-1,sizeof head);  
  65.     scanf(”%d%d%d”,&r,&c,&d);  
  66.       
  67.     for(int i=1;i<=r;i++)scanf(“%s”,s1[i]);  
  68.     for(int i=1;i<=r;i++)scanf(“%s”,s2[i]);  
  69.       
  70.     for(int i=1;i<=r;i++)  
  71.         for(int j=0;j<c;j++)if(s1[i][j]!=‘0’)  
  72.             for(int k=1;k<=r;k++)  
  73.                 for(int l=0;l<c;l++)if(s1[k][l]!=‘0’&&(i!=k||j!=l))  
  74.                     if(abs(i-k)+abs(j-l)<=d)  
  75.                         add(i*c+j,(k*c+l)+r*c+r*c,INF);  
  76.     for(int i=1;i<=r;i++)  
  77.         for(int j=0;j<c;j++)if(s1[i][j]!=‘0’)add((i*c+j)+r*c+r*c,i*c+j,s1[i][j]-‘0’);  
  78.     for(int i=1;i<=r;i++)  
  79.         for(int j=0;j<c;j++)  
  80.             if(s2[i][j]==‘L’){  
  81.                 add(START,(i*c+j)+r*c+r*c,1);  
  82.                 tot++;  
  83.             }  
  84.     for(int i=1;i<=r;i++)  
  85.         for(int j=0;j<c;j++)  
  86.             if(s1[i][j]!=‘0’&&((i<=d||(r-i+1)<=d)||(j+1<=d||(c-j<=d)))){  
  87.                 add(i*c+j,END,tot);  
  88.             }     
  89.     s=START;  
  90.     t=END;  
  91.     int sum=0;  
  92.     while(BFS()){  
  93.         int flow=0;  
  94.         while(flow=dfs(s,INF))sum+=flow;  
  95.     }  
  96.     printf(”%d\n”,tot-sum);  
  97.     return 0;  
  98. }  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值