【题目】
题目描述:
在一个 r r r 行 c c c 列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。
每行每列中相邻石柱的距离为 1 1 1,蜥蜴的跳跃距离是 d d d,即蜥蜴可以跳到平面距离不超过 d d d 的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减 1 1 1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为 1 1 1,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。
输入格式:
输入第一行为三个整数 r r r, c c c, d d d,即地图的规模与最大跳跃距离。
以下 r r r 行为石竹的初始状态, 0 0 0 表示没有石柱, 1 1 1~ 3 3 3 表示石柱的初始高度。
以下 r r r 行为蜥蜴位置,“L” 表示蜥蜴,“.” 表示没有蜥蜴。
输出格式:
输出仅一行,包含一个整数,即无法逃离的蜥蜴总数的最小值。
样例数据:
输入
5 8 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........
输出
1
备注:
【数据范围】
100 % 100\% 100% 的数据满足: 1 ≤ r , c ≤ 20 1≤r,c≤20 1≤r,c≤20, 1 ≤ d ≤ 3 1≤d≤3 1≤d≤3 。
【分析】
一道不错的网络流建模题。
由于每个跳出柱子后高度会减 1 1 1,这相当于限制了每个柱子的出边容量,很容易想到拆点。
为了方便,用 i i i 表示原柱子的话,就用 i ′ i' i′ 表示拆点后的柱子
那么就以下面的四种规则建模:
- 从汇点向每个蜥蜴所在的位置连容量为 1 1 1 的边。
- 从每个柱子 i i i 向拆点后的柱子 i ′ i' i′ 连容量为它本身的高度的边。
- 如果从柱子 i i i 能跳到柱子 j j j,就从 i ′ i' i′ 向 j j j 连容量为 i n f inf inf 的边,表示能从 i → i ′ → j i\to i'\to j i→i′→j(由于拆点后已经限制了柱子的出边容量,这时的容量可以为 i n f inf inf)。
- 从可以跳出地图的柱子 i ′ i' i′ 向汇点连 i n f inf inf 的边(连 i n f inf inf 的原因同 3 3 3)。
那么这样连完边之后跑最大流就是答案了。
【代码】
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10005
#define M 1000005
#define inf (1ll<<31ll)-1
using namespace std;
int n,m,k,s,t,num,tot=1;
int v[M],w[M],nxt[M];
int d[N],f[N],first[N];
char h[25][25],Map[25][25];
bool can[25][25];
int id(int i,int j){return (i-1)*m+j;}
void add(int x,int y,int z)
{
nxt[++tot]=first[x];
first[x]=tot,v[tot]=y,w[tot]=z;
}
bool bfs(int s)
{
int x,y,i;
memset(d,-1,sizeof(d));
memcpy(f,first,sizeof(f));
queue<int>q;q.push(s);d[s]=0;
while(!q.empty())
{
x=q.front();q.pop();
for(i=f[x];i;i=nxt[i])
{
y=v[i];
if(w[i]&&d[y]==-1)
{
d[y]=d[x]+1;
q.push(y);
}
}
}
return d[t]!=-1;
}
int dinic(int now,int flow)
{
if(now==t) return flow;
int x,delta,ans=0;
for(int &i=f[now];i;i=nxt[i])
{
x=v[i];
if(w[i]&&d[x]==d[now]+1)
{
delta=dinic(x,min(flow,w[i]));
w[i]-=delta,w[i^1]+=delta,flow-=delta,ans+=delta;
if(!flow) return ans;
}
}
return ans;
}
int dist(int x1,int y1,int x2,int y2){return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);}
void link(int x,int y,int val)
{
int i,j;
for(i=0;i<=n+1;++i)
{
for(j=0;j<=m+1;++j)
{
int dis=dist(x,y,i,j);
if(dis<=k*k)
{
if(i==0||i==n+1||j==0||j==m+1) can[x][y]=true;
else if(id(x,y)!=id(i,j)&&h[i][j]!='0') add(m*n+id(x,y),id(i,j),inf),add(id(i,j),m*n+id(x,y),0);
}
}
}
add(id(x,y),m*n+id(x,y),val),add(m*n+id(x,y),id(x,y),0);
if(can[x][y]) add(m*n+id(x,y),t,inf),add(t,m*n+id(x,y),0);
}
int main()
{
int i,j,ans=0;
scanf("%d%d%d",&n,&m,&k);
s=0,t=2*n*m+1;
for(i=1;i<=n;++i) scanf("%s",h[i]+1);
for(i=1;i<=n;++i) scanf("%s",Map[i]+1);
for(i=1;i<=n;++i)
{
for(j=1;j<=m;++j)
{
if(h[i][j]!='0') link(i,j,h[i][j]-'0');
if(Map[i][j]=='L') num++,add(s,id(i,j),1),add(id(i,j),s,0);
}
}
while(bfs(s)) ans+=dinic(s,inf);
printf("%d",num-ans);
return 0;
}