[bzoj 1066][SCOI2007]蜥蜴

11 篇文章 0 订阅
4 篇文章 0 订阅

Description

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

Input

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

Output

输出仅一行,包含一个整数,即无法逃离的蜥蜴总数的最小值。

Sample Input

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

Sample Output

1

HINT

100%的数据满足:1<=r, c<=20, 1<=d<=3

题解

此题的难点就难在建图

1.对于每个点 拆成两个点 一个入点 一个出点 连一条权值为高度的边

2.对于每个有蜥蜴的点 超级源点 与他连一条 权值为1的边 (只能有一个蜥蜴)

3.对于可以跳出地图的点 把他的出点与超级汇 连一条inf的边

4.对于每可以跳到的点 起跳的出点 和到达的入点连一条inf的边

剩下的就是跑最大流 然后用总数减去最大流即可

//http://www.lydsy.com/JudgeOnline/problem.php?id=1066
//较难的一题最大流 
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define inf 0x7ffffff
using namespace std;
struct edge{int to,w,next;}e[500001];
int n,m,d,cnt=1,num=0;
int start=0,end=1000; //超级源点 和 超级汇点
int mp[30][30],bh[30][30];
int dis[1500],head[1500];
void ini(int x,int y,int z){
	e[++cnt].to=y;e[cnt].w=z;e[cnt].next=head[x];head[x]=cnt;
}
void insert(int x,int y,int z){
	ini(x,y,z);ini(y,x,0);
}
queue<int>q;
bool bfs(){
	q.push(start);
	memset(dis,-1,sizeof(dis));
	dis[start]=0;
	while(!q.empty()){
		int k=q.front();q.pop();
		for(int i=head[k];i;i=e[i].next){
			int kk=e[i].to;
			if(dis[kk]==-1&&e[i].w>0){
				dis[kk]=dis[k]+1;q.push(kk);
			}
		}
	}
	if(dis[end]==-1) return false;
	return true;
}
int dfs(int x,int mn){
	if(x==end) return mn;
	for(int i=head[x];i;i=e[i].next){
		int k=e[i].to;int a;
		if(dis[k]==dis[x]+1&&e[i].w>0&&(a=dfs(k,min(e[i].w,mn))))
		{
			e[i].w-=a;
			e[i^1].w+=a;
			return a;
		}
	}
	return 0;
}
bool excape(int x,int y)
{
    return min(min(x,n+1-x),min(y,m+1-y))<=d;
}
bool judge(int x1,int y1,int x2,int y2){
	if(x1==x2&&y1==y2) return false;
	return (mp[x1][y1]&&mp[x2][y2]&&(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)<=d*d);
}
void dinic(){
	int ans=0;
	while(bfs()){
		int a;
		while(a=dfs(start,inf))
			ans+=a;
	}
	printf("%d",num-ans);
}
int main(){
	scanf("%d%d%d",&n,&m,&d);
	char ch[30];
	int tot=0;
	for(int i=1;i<=n;i++){
		scanf("%s",ch+1);
		for(int j=1;j<=m;j++){
			mp[i][j]=ch[j]-'0';
			bh[i][j]=++tot;
		}
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=m;j++)
		{
			if(ch[j]=='L'){
				insert(0,bh[i][j],1);
				num++;
			}
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			if(mp[i][j])
			insert(bh[i][j],bh[i][j]+400,mp[i][j]);//拆点
			if(excape(i,j)) insert(bh[i][j]+400,end,inf);//能走的话就连超级汇点
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int t1=max(1,i-d);t1<=min(i+d,n);t1++)
				for(int t2=max(1,j-d);t2<=min(j+d,m);t2++)
					if(judge(i,j,t1,t2)) insert(bh[i][j]+400,bh[t1][t2],inf);
	 dinic();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值