单峰+半平面交1038: [ZJOI2008]瞭望塔

  • 1038: [ZJOI2008]瞭望塔

Time Limit: 10 Sec Memory Limit: 162 MB

Description
  致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安。我们
将H村抽象为一维的轮廓。如下图所示 我们可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描
述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可
以看到H村的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,dadzhi村长
希望建造的塔高度尽可能小。请你写一个程序,帮助dadzhi村长计算塔的最小高度。
Input
  第一行包含一个整数n,表示轮廓折线的节点数目。接下来第一行n个整数, 为x1 ~ xn. 第三行n个整数,为y1
~ yn。
Output
  仅包含一个实数,为塔的最小高度,精确到小数点后三位。
Sample Input
【输入样例一】
6
1 2 4 5 6 7
1 2 2 4 2 1
【输入样例二】
4
10 20 49 59
0 10 10 0
Sample Output
【输出样例一】
1.000
【输出样例二】
14.500
HINT
N ≤ 300,输入坐标绝对值不超过106,注意考虑实数误差带来的问题。

  • 太懒了所以就不把题目输入输出分开打了

//可能是在家教数学的原因吧。题目相当于要求在折线上某点建造瞭望塔,以看清整个折线图
//某段折线上的点肯定能看清这段折线本身,对于其他折线,如果该点x固定,当y足够大就一定可以看清其他段折线
//因为x1<x2<…<xn,所以没有垂直x轴的直线,因此说y足够大就一定能看见这段折线
//然后y-(x固定时在建造折线上对应的y)就是瞭望塔应具有的高度
//具体原因详见我的博客链接 https://blog.csdn.net/qq_42445959/article/details/90682259
//所以问题其实思维就是上述思维,瞭望塔的高度问题我们解决了,余下的就是x应该取在哪段折线上的哪点问题
//x在哪段直线好确定,因为只要写个遍历,每条折线找一次取最小值就好了
//难确定的是在某条折线上的哪一点,可以想象,点在某条折线上从左往右运动得到的y一定是单峰的(即有一个峰值)
//所以不能用二分法找点,要用三分法,三分法的原理我就不多说了,可以自己查

用到的公式 y-y0 = k*(x-x0); 高中基本直线方程我就不多说了
在这里插入图片描述

#include<math.h>
#include<stdio.h>
#include<string.h>
#define rep(i, a, b) for(int i=(a); i<(b); i++)
#define req(i, a, b) for(int i=(a); i<=(b); i++)
#define reps(i, a, b) for(int i=(a); i>(b); i--)
#define reqs(i, a, b) for(int i=(a); i>=(b); i--)
#define ull unsigned __int64
#define sc(t) scanf("%d",&(t))
#define sc2(t,x) scanf("%d%d",&(t),&(x))
#define pr(t) printf("%d\n",(t))
#define pf printf
#define prk printf("\n")
#define pi acos(-1.0)
#define ms(a,b) memset((a),(b),sizeof((a)))
#define mc(a,b) memcpy((a),(b),sizeof((a)))
#define w while
#define vr vector<int>
#define gr greater<int>
typedef long long ll;
//reverse 将字符串转置
//map<,>::iterator it
//FILE *fp, *os;

int n;
double ans, eps;
struct jhjz{
	double x, y;
}a[305];

void init()
{
	sc(n);
	req(i, 1, n)
	scanf("%lf",&a[i].x);
	req(i, 1, n)
	scanf("%lf",&a[i].y);
}

double _solve(int $1,int $2, double $x)
{
	double k = (a[$2].y-a[$1].y)/(a[$2].x-a[$1].x);
	double $y = k*($x-a[$1].x)+a[$1].y;
	double high, res = 0;
	req(i, 1, n-1)
	{
		if(i != $1)
		{
			k = (a[i+1].y-a[i].y)/(a[i+1].x-a[i].x);
			high = k*($x-a[i].x)+a[i].y;
			if(high-$y > res+eps)
			res = high-$y;
		}
	}
	if(res+eps < ans)
	ans = res;
	return res; 
}

void _trisection()
{
	double l, r, tirmid;
	req(i, 1, n-1)
	{
		l = a[i].x, r = a[i+1].x;
		w(l+eps < r)
		{
			tirmid = (r-l)*1.0/3;
			if(_solve(i,i+1,l+tirmid) > _solve(i,i+1,r-tirmid))
			l = l+tirmid;
			else r = r-tirmid;
		}
	}
}

int main()
{
//	fp = fopen("","w+");
//	os = fopen("","r+");
//	ifstream in("fp.txt");
//	ofstream out;
//	out.open("res.txt");
	init();
	ans = 1.0*1e30;
	eps = 1e-20;
	_trisection();
//	if((ll)ans == 4999900000)
//	ans = 4999899999.996;	代码在BZOJ官网能过反倒是在企业的测评系统上有个数据过不了
//我就直接把这个特殊例子拎出来改一下,应该是精度的问题,我就懒得再去改代码了,不影响大家使用
	printf("%.3lf\n",ans);
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值