【bzoj 1340】 Escape逃跑问题 【Baltic2007】

Description

战犯们企图逃离监狱,他们详细地计划了如何逃出监狱本身,逃出监狱之后他们希望在附近的一个村子里找到掩护。村子(下图中的B)和监狱(图中的A)中间有一个峡谷,这个峡谷也是有士兵守卫的。守卫峡谷的士兵们坐在岗哨上很少走动,每个士兵的观察范围是100米。士兵所处位置决定了战犯们能否安全通过峡谷,安全通过的条件就是在任何时刻战犯们距离最近的士兵大于100米。 给定峡谷的长、宽和每个士兵在峡谷中的坐标,假定士兵的位置一直保持不变,请你写一个程序计算战犯们能否不被士兵发现,顺利通过峡谷。如果不能,那么战犯们最少需要消灭几个士兵才能安全通过峡谷(无论士兵是否被另一个士兵看到,他都可以被消灭)。 

Input

第一行有三个整数L、W和N,分别表示峡谷的长度、宽度和士兵的人数。接下来的N行,每行两个整数Xi和Yi,表示第i个士兵在峡谷的坐标(0 <= Xi <= L, 0 <= Yi <= W),坐标以米为单位,峡谷的西南角坐标为(0, 0),东北角坐标为(L, W),见上图。注意:通过峡谷可以从(0, ys)(0 <= ys <= W)到(L, ye)(0 <= ye <= W),其中ys, ye不一定是整数。

Output

只有一行,为一个整数,即安全通过峡谷需要消灭的士兵的人数,如果不需要消灭任何士兵,则输出0。

Sample Input

130 340 5
10 50
130 130
70 170
0 180
60 260

Sample Output

1

HINT

1 <= W <= 50,000 1 <= L <= 50,000 1 <= N <= 250

对于这道题,首先我们可以发现最后结果一定是一条穿过若干个类似下图的由士兵组成的集合,所以我们考虑设置超级源点和超

级汇点,对于每一个士兵U_{i},将其拆为两个点U_{i1}U_{i2},在两个点之间连一条权值为1的边,如果他的观察范围达到峡谷上端,则将U_{i1}与超级源连一条权值为INF的边,如果他的观察范围达到峡谷下端,则将U_{i2}与超级汇连连一条权值为INF的边,对于任意两个士兵U_{i}U_j,如果他们的观察范围相交,则在U_{i2}U_{j1}之间连一条权值为INF的边,那么最后的结果就是这张图的最小割,下面是程序:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
const int N=200005,INF=0x7fffffff;
struct queue{
	int l,r,a[N];
	void clear(){
		l=0,r=1;
	}
	bool empty(){
		return l+1==r;
	}
	void push(int x){
		a[r]=x;
		r=(r+1)%N;
	}
	void pop(){
		l=(l+1)%N;
	}
	int front(){
		return a[(l+1)%N];
	}
}q;
struct Point{
	int x,y;
}a[N];
struct edge{
	int v,w,next;
}e[N];
int head[N],k,d[N],s,t;
int abs(int n){
	return n>0?n:-n;
}
int dis(Point a,Point b){
	int x=a.x-b.x,y=a.y-b.y;
	return x*x+y*y;
}
inline void add(int u,int v,int w){
	e[k]=(edge){v,w,head[u]};
	head[u]=k++;
	e[k]=(edge){u,0,head[v]};
	head[v]=k++;
}
bool BFS(){
	q.clear();
	memset(d,0,sizeof(d));
	q.push(s);
	d[s]=1;
	int i,u;
	while(!q.empty()){
		u=q.front();
		q.pop();
		for(i=head[u];i!=-1;i=e[i].next){
			if(!d[e[i].v]&&e[i].w){
				d[e[i].v]=d[u]+1;
				q.push(e[i].v);
			}
		}
	}
	return d[t];
}
int dfs(int u,int f){
	if(u==t||f==0){
		return f;
	}
	int i,t,use=0;
	for(i=head[u];i!=-1;i=e[i].next){
		if(d[e[i].v]==d[u]+1&&(t=dfs(e[i].v,min(f-use,e[i].w))>0)){
			e[i].w-=t;
			e[i^1].w+=t;
			use+=t;
			if(use==f){
				return f;
			}
		}
	}
	return use;
}
int dinic(){
	int ans=0;
	while(BFS()){
		ans+=dfs(s,INF);
	}
	return ans;
}
int main(){
	int n,m,k,i,j;
	scanf("%d%d%d",&n,&m,&k);
	s=0,t=2*k+1;
	memset(head,-1,sizeof(head));
	for(i=1;i<=k;i++){
		scanf("%d%d",&a[i].x,&a[i].y);
		if(a[i].y<=100){
			add(s,i,INF);
		}
		if(abs(a[i].y-m)<=100){
			add(i+k,t,INF);
		}
		add(i,i+k,1);
	}
	for(i=1;i<=k;i++){
		for(j=1;j<=k;j++){
			if(i!=j&&dis(a[i],a[j])<=40000){
				add(i+k,j,INF);
			}
		}
	}
	printf("%d\n",dinic());
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值