【SCOI 2007】蜥蜴

【题目】

传送门

题目描述:

在一个 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 1r,c20 1 ≤ d ≤ 3 1≤d≤3 1d3


【分析】

一道不错的网络流建模题。

由于每个跳出柱子后高度会减 1 1 1,这相当于限制了每个柱子的出边容量,很容易想到拆点。

为了方便,用 i i i 表示原柱子的话,就用 i ′ i' i 表示拆点后的柱子

那么就以下面的四种规则建模:

  1. 从汇点向每个蜥蜴所在的位置连容量为 1 1 1 的边。
  2. 从每个柱子 i i i 向拆点后的柱子 i ′ i' i 连容量为它本身的高度的边。
  3. 如果从柱子 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 iij(由于拆点后已经限制了柱子的出边容量,这时的容量可以为 i n f inf inf)。
  4. 从可以跳出地图的柱子 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值