bzoj1038[ZJOI2008]瞭望塔

正解:半平面交

但是我们可以发现这么多线搞半平面的交集是一个半凸壳,没错于是我们就可以借用某一道好像是最大可视面积(其实是HNOI2008水平可视直线)还是什么的题的做法直接维护这个图形

然后我们就可以在每条线的交点处计算上下差值,至于为什么是交点……简单动态规划的最优值一定在某一个端点处取得大概是这样(其实我们可以用单调性来证明,毕竟线性函数)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#define LL long long
#define LF double
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define down(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
#define N 305
#define eps 1e-8
struct P
{
	double x,y;
}d[N],l[N];
int n,m;
LL read()
{
	LL d=0,f=1;char s=getchar();
	while (s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while (s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
	return d*f;
}

LF operator*(P a,P b)
{
	return a.x*b.y-a.y*b.x;
}

LF operator/(P a,P b)
{
	return a.x*b.x+a.y*b.y;
}

P operator+(P a,P b)
{
	P t;
	t.x=a.x+b.x; t.y=a.y+b.y;
	return t;
}

P operator-(P a,P b)
{
	P t;
	t.x=a.x-b.x; t.y=a.y-b.y;
	return t;
}

bool operator==(P a,P b)
{
	return (a.x==b.x)&&(a.y==b.y);
}

LF dis(P a,P b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y*b.y)*(a.y*b.y));
}

P make(P a,P b)
{
	P t;
	t.x=(b.y-a.y)/(b.x-a.x);
	t.y=a.y-a.x*t.x;
	return t;
}

bool sortcom(P a,P b)
{
	if (fabs(a.x-b.x)<=eps) return a.y<b.y;
	return a.x<b.x;
}

double calx(int a,int b)
{
	return (l[b].y-l[a].y)/(l[a].x-l[b].x);
}

double ans=0.0;
int s[N],stop=0;
double yup(double x)
{
	double t=0.0;
	fo(i,1,stop) t=max(t,l[s[i]].x*x+l[s[i]].y);
	return t;
}

double ydown(double x)
{
	int po=n;
	double t=0.0;
	fo(i,1,n-1)
	if (d[i].x<x&&x<d[i+1].x) 
	{
		po=i;
		break;
	}
	if (po==n) return d[n].y;
	P lin=make(d[po],d[po+1]);
	t=lin.x*x+lin.y;
	return t;
}

double crossx(P a,P b)
{
	return (b.y-a.y)/(a.x-b.x);
}

void checks()
{
	fo(i,1,stop)
	cout<<l[s[i]].x<<' '<<l[s[i]].y<<endl;
	cout<<endl;
}

int main()
{
	n=read();
	fo(i,1,n) d[i].x=read();
	fo(i,1,n) d[i].y=read();
	fo(i,1,n-1) l[i]=make(d[i+1],d[i]);
	m=n-1;
	sort(l+1,l+m+1,sortcom);
//	checkl();
    stop=0;
	s[++stop]=1;
	fo(i,2,m)
	{
		while (fabs(l[s[stop]].x-l[i].x)<=eps) stop--;
		while (stop>1&&calx(s[stop-1],s[stop])>=calx(s[stop],i)) 
			stop--;
		s[++stop]=i;
	}
	cout<<setiosflags(ios::fixed);
    cout.precision(3);
//    checks();
	ans=1e10;
	fo(i,1,n)
	{
		ans=min(ans,yup(d[i].x)-d[i].y);
//		cout<<ans<<endl;
	}
//	cout<<endl;
	fo(i,1,stop-1)
	{
		double t=crossx(l[s[i]],l[s[i+1]]);
		double ty=l[s[i]].x*t+l[s[i]].y;
		ans=min(ans,ty-ydown(t));
//		cout<<ans<<endl;
	}
	cout<<ans<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值