【BOI2007】逃跑问题 (BSOI2344)

Description

  战犯们企图逃离监狱,他们详细地计划了如何逃出监狱本身,逃出监狱之后他们希望在附近的一个村子里找到掩护。村子和监
狱中间有一个峡谷,这个峡谷也是有士兵守卫的。守卫峡谷的士兵们坐在岗哨上很少走动,每个士兵的观察范围是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 1700 180
60 260

Sample Output

1

Hint

【数据范围】
1≤W≤50000
1≤L≤50000
1≤N≤250
       经典的点限制网络流。首先建图,将每一个士兵拆成两个点,并且容量为1。接着判断两个士兵之间距离是否小于200(一个人范
围100,两个间隔就是200了。。。),如果有连接,就将其连上无向边,容量为oo。同时,我们的士兵如果和山谷的边界距离小于
100,则将其连接上边界代表的源点和汇点。最后简单的SAP一遍最大流就好了,最大即为最小割,也就是需要击毙的士兵人数。
蒟蒻的代码因为用了一种诡异的无向边添加方式,所以需要8*n*n的空间。。。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int N,W,L,n;
const int inf=0x3f3f3f3f;
struct Pipe{int next,to,flow;}pipe[500005];
struct psn{int x,y;}psn[2550];//士兵坐标 
int h[50005],cnt=1;
int Gap[50005],dis[50005];
int ans=0;
inline void add(int x,int y,int z){
	pipe[++cnt].to=y;pipe[cnt].next=h[x];h[x]=cnt;pipe[cnt].flow=z;
	pipe[++cnt].to=x;pipe[cnt].next=h[y];h[y]=cnt;pipe[cnt].flow=0;
	return ;
}//双向加边 
inline int read(){
	char c;int rec=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec;
}
inline bool cal(int x,int y){
	int dis=(psn[x].x-psn[y].x)*(psn[x].x-psn[y].x)+(psn[x].y-psn[y].y)*(psn[x].y-psn[y].y);
	if(dis<=40000)return 1;
	return 0;
}//用整型计算距离,避免浮点型的精度误差(虽然本来就不多) 
inline int SAP(int v,int maxflow){
	if(v==2*N+1)return maxflow;
	int temp=maxflow,i,j,p;
	for(i=h[v];i;i=pipe[i].next){
		j=pipe[i].to;
		if(pipe[i].flow&&dis[v]==dis[j]+1){
			p=SAP(j,min(pipe[i].flow,temp));
			temp-=p;pipe[i].flow-=p;pipe[i^1].flow+=p;
			if(temp==0||dis[0]==n)return maxflow-temp;
		}
	}
	if(--Gap[dis[v]]==0)dis[0]=n;
	else Gap[++dis[v]]++;
	return maxflow-temp;
}
int main(){
	L=read();W=read();N=read();
	int i,j,k;
	for(i=1;i<=N;i++){
	   psn[i].x=read();psn[i].y=read();
	   add(i,i+N,1);//拆点 
	   if(psn[i].y<=100)add(0,i,inf);//起始源点 
	   if(psn[i].y+100>=W)add(i+N,2*N+1,inf);//结束汇点 
	}
	n=N*2+2;//总点数 
	for(i=1;i<=N;i++)
		for(j=i+1;j<=N;j++)
			if(cal(i,j))add(i+N,j,inf),add(j+N,i,inf);//极大浪费空间的双向加边 
	Gap[0]=n;
	while(dis[0]<n)ans+=SAP(0,inf);//累加得到最大流,即最小割 
	cout<<ans;//若可以直接穿过山谷,则不存在S-T的线路,则最大流必定为0,无需dfs特判 
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值