[SCOI2007] 蜥蜴
题目描述
在一个 \(r\) 行 \(c\) 列的网格地图中有一些高度不同的石柱,第 \(i\) 行 \(j\) 列的石柱高度为 \(h_{i,j}\)。
一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。
每行每列中相邻石柱的距离为 \(1\),蜥蜴的跳跃距离是 \(d\),即蜥蜴可以跳到平面距离不超过 \(d\) 的任何一个石柱上。
石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减 \(1\)(如果仍然落在地图内部,则到达的石柱高度不变)。
如果该石柱原来高度为 \(1\),则蜥蜴离开后消失,以后其他蜥蜴不能落脚。
任何时刻不能有两只蜥蜴在同一个石柱上。
对于 \(100\%\) 的数据满足:\(1\le r,c\le20\),\(1\le d\le 4\),\(1\le h\le 3\)。
题目分析
最近在做网络流的题,也是因为考试遇到了,所以学了一下网络流。但是很菜,也就只会板子了。
学了之后发现,只要知道拆点这个方法,这道题就很好想了。
因为 限制条件:通过次数 是在点上,所以把每个值不为零的点分配两个编号,一个入点,一个出点。柱子高度为零的点忽略不管。
-
入点到出点连边,容量为这个点柱子高度,即可通过的蜥蜴数。
-
然后每两个可以到达的点的出点都与对方的入点连边,容量正无穷。
-
每个可以到达地图外的点与汇点连边,容量正无穷。
-
原点到每个有蜥蜴的点连边,容量为 1 。
这样图的模型就建好了,只需要再跑一个最大流就解决了。得到的最大流就是可以逃离的蜥蜴数,用总量减一下就是答案了。
Code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=4005,M=4e5,INF=0x3f3f3f3f;
int nxt[M],h[N],ver[M],w[M],co=1;
int de[N],no[N*2];
int n,m,d,s,t;
int mp[25][25],id[25][25],c=1;
inline int read(){
int sum=0,f=1;char a=getchar();
while(a<'0' || a>'9'){if(a=='-') f=-1;a=getchar();}
while(a>='0' && a<='9') sum=sum*10+a-'0',a=getchar();
return sum*f;
}
void add(int x,int y,int z){
nxt[++co]=h[x],h[x]=co,ver[co]=y,w[co]=z;
nxt[++co]=h[y],h[y]=co,ver[co]=x,w[co]=0;
}
bool ch(int x,int y,int x1,int y1){
int t1=abs(x-x1),t2=abs(y-y1);
double tmp=sqrt(1.0*t1*t1+1.0*t2*t2);
if(tmp<=d) return 1;
return 0;
}
bool bfs(){
memset(de,0,sizeof de);
queue<int> q;
q.push(s);
de[s]=1,no[s]=h[s];
while(q.size()){
int x=q.front();
q.pop();
for(int i=h[x];i;i=nxt[i]){
int y=ver[i];
if(w[i] && !de[y]){
q.push(y);
no[y]=h[y];
de[y]=de[x]+1;
if(y==t) return 1;
}
}
}
return 0;
}
int dfs(int x,int flow){
if(x==t) return flow;
int rest=flow,k,i;
for(i=h[x];i && rest;i=nxt[i]){
int y=ver[i];
if(de[y]==de[x]+1 && w[i]){
k=dfs(y,min(rest,w[i]));
if(!k) de[y]=0;
w[i]-=k;
w[i^1]+=k;
rest-=k;
}
}
no[x]=i;
return flow-rest;
}
int main(){
char a;int tot=0;
n=read(),m=read(),d=read();
memset(mp,0x3f,sizeof mp);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) cin>>a,mp[i][j]=a-'0';
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(mp[i][j]){
id[i][j]=++c;++c;
add(id[i][j],c,mp[i][j]);
}
s=++c,t=++c;
memset(de,0,sizeof de);
for(int x=0;x<=n;++x)
for(int y=0;y<=m;++y)
for(int x1=1;x1<=n+1;++x1)
for(int y1=1;y1<=m+1;++y1)
if((x!=x1 || y!=y1) && mp[x][y] && mp[x1][y1] && ch(x,y,x1,y1)){
if((!x || !y) && (x1==n+1 || y==m+1)) continue;
if(!x || !y){
if(!de[id[x1][y1]]){de[id[x1][y1]]=1;add(id[x1][y1]^1,t,INF);}
}
else if(x1==n+1 || y1==m+1){
if(!de[id[x][y]]){de[id[x][y]]=1;add(id[x][y]^1,t,INF);}
}
else
add(id[x][y]^1,id[x1][y1],INF);
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
cin>>a;
if(a=='L') add(s,id[i][j],1),++tot;
}
int ans=0,flow=0;
while(bfs()){
while(flow=dfs(s,INF)) ans+=flow;
}
cout<<tot-ans;
return 0;
}
结语
这道题思路其实不难,但是建图的代码实现有些细节要自己慢慢调。
多交几次,或者去 BZOJ 白嫖数据也可以