bzoj1020 安全的航线flight

        这道题目的思路,可以见莫涛的论文《迭代思想的应用》

       虽然已经很详细了,这里还是讲一下吧。

       首先把原来的所有折线中的线段都加入队列。然后对于条线段(a,b),进行操作:

       首先找出a到陆地的最近的点x(如果a在陆地内或边上,则x=a),以及和b最近的点y。然后考虑a,b上的一个特殊点p,使得px=py,简单的可以二分得到(或者用中垂线也可以?没有试过)。那么对于(a,b)上的任意一个点q,显然有q到陆地的最近距离<px(py),因为如果q在(a,p)上,那么qx<px;q在(p,b)上同理。因此线段(a,b)与陆地的最大值显然不会超过px。那么如果px<当前的ans,这条线段就可以无视辣!!否则拆成两条线段(a,p)(p,b)加入队列。

       至于为什么时间复杂度是对的。我也不知道。。另外这道题目好像一片4k+。。好可怕啊。。然而写起来发现还是比较简单的也就2.7k+(我又来秀弱~\(≧▽≦)/~辣)(关键是DeBug)

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define eps 1e-4
#define N 105
#define M 1000005
using namespace std;

int n,m; double ans; struct point{ double x,y; }a[N];
struct disp{ point p; double dis; };
struct line{ point p1,p2; }h[M];
point operator -(point u,point v){
	point t; t.x=u.x-v.x; t.y=u.y-v.y; return t;
}
double dot(point u,point v){ return u.x*v.x+u.y*v.y; }
double crs(point u,point v){ return u.x*v.y-u.y*v.x; }
double dist(point u,point v){ return sqrt(dot(u-v,u-v)); }
bool upon(point u,point v,point w){
	return !crs(v-u,w-u) && (u.x-v.x)*(u.x-w.x)<=0 && (u.y-v.y)*(u.y-w.y)<=0;
}
bool havitr(point u,point v,point s,point t){
	return crs(v-u,s-u)*crs(v-u,t-u)<=0 && crs(t-s,u-s)*crs(t-s,v-s)<=0;
}
point getitr(point u,point s,point v,point t){
	double tmp=crs(t,u-v)/crs(s,t);
	u.x+=s.x*tmp; u.y+=s.y*tmp; return u;
}
disp lss(disp u,disp v){ return (u.dis<v.dis)?u:v; }
struct land{
	int tot; point p[N];
	void init(){
		scanf("%d",&tot); int i;
		for (i=1; i<=tot; i++) scanf("%lf%lf",&p[i].x,&p[i].y);
		p[tot+1]=p[1];
	}
	bool inside(point t){
		int i,sum=0;
		for (i=1; i<=tot; i++)
			if (upon(t,p[i],p[i+1])) return 1;
		point s=t; s.x=1e5;
		for (i=1; i<=tot; i++)
			if (havitr(s,t,p[i],p[i+1])) sum++;
		return sum&1;
	}
}b[N];
disp nearst(point u,point v,point w){
	disp t;
	if (v.x==w.x && v.y==w.y) t.p=v; else
	if (dot(u-v,w-v)<=0) t.p=v; else
	if (dot(u-w,v-w)<=0) t.p=w; else{
		point s=v-w; swap(s.x,s.y); s.x=-s.x;
		t.p=getitr(u,s,v,w-v);
	}
	t.dis=dist(t.p,u); return t;
}
point updata(point t){
	disp tmp; int i,j;
	for (i=1; i<=m; i++)
		if (b[i].inside(t)) return t;
	tmp.dis=1e20;
	for (i=1; i<=m; i++)
		for (j=1; j<=b[i].tot; j++)
			tmp=lss(tmp,nearst(t,b[i].p[j],b[i].p[j+1]));
	ans=max(ans,tmp.dis); return tmp.p;
}
int main(){
	scanf("%d%d",&m,&n); int i;
	for (i=1; i<=n; i++) scanf("%lf%lf",&a[i].x,&a[i].y);
	for (i=1; i<=m; i++) b[i].init();
	int head=0,tail=0;
	for (i=1; i<n; i++){
		h[++tail].p1=a[i]; h[tail].p2=a[i+1];
		updata(a[i]);
	}
	updata(a[n]);
	while (head!=tail){
		head=head%M+1;
		point l=h[head].p1,r=h[head].p2,u=updata(l),v=updata(r),mid;
		while (dist(l,r)>eps){
			mid.x=(l.x+r.x)/2; mid.y=(l.y+r.y)/2;
			if (dist(mid,u)<dist(mid,v)) l=mid; else r=mid;
		}
		double tmp=dist(l,u); updata(l);
		if (tmp>ans+eps){
			tail=tail%M+1; h[tail].p1=h[head].p1; h[tail].p2=mid;
			tail=tail%M+1; h[tail].p1=mid; h[tail].p2=h[head].p2;
		}
	}
	printf("%.2f\n",ans);
	return 0;
}


by lych

2016.3.2

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值