bzoj千题计划126:bzoj1038: [ZJOI2008]瞭望塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1038

本题可以使用三分法

将点按横坐标排好序后

对于任意相邻两个点连成的线段,瞭望塔的高度 是单峰函数,而且是下凸函数

感性理解单峰就是

瞭望塔建的靠左,为了能看到右边的,要高一点

瞭望塔建的靠右,为了能看到左边的,要高一点

所以

枚举所有线段,三分线段上建造瞭望塔的位置,所有线段上的瞭望塔高度取最小

#include<cmath>
#include<cstdio>
#include<algorithm>

using namespace std;

#define N 301

const double eps=1e-7;

int n;

struct POINT
{
    double x,y;
}e[N];

struct LINE
{
    double k,b;
    bool exit;
}l[N];

bool cmp(POINT p,POINT q)
{
    return p.x<q.x;
}

double cal(double x,double y)
{
    double tmp=0;
    for(int i=1;i<n;++i)
    {
        if(!l[i].exit) continue;
        tmp=fmax(tmp,l[i].k*x+l[i].b-y);
    }
    return tmp;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%lf",&e[i].x);
    for(int i=1;i<=n;++i) scanf("%lf",&e[i].y);
    sort(e+1,e+n+1,cmp);
    for(int i=1;i<n;++i)
    {
        if(fabs(e[i].x-e[i+1].x)<eps) continue;
        l[i].k=(e[i].y-e[i+1].y)/(e[i].x-e[i+1].x);
        l[i].b=e[i].y-e[i].x*l[i].k;
        l[i].exit=true;
    }
    double L,R,mid1,mid2;
    int T;
    double ans=1e20;
    for(int i=1;i<n;++i)
    {
        if(!l[i].exit) continue;
        T=100; L=e[i].x; R=e[i+1].x;
        while(T--)
        {
            mid1=L+(R-L)/3; mid2=R-(R-L)/3;
            if(cal(mid1,l[i].k*mid1+l[i].b)<cal(mid2,l[i].k*mid2+l[i].b)) R=mid2;
            else L=mid1;
        }
        ans=fmin(ans,cal(mid1,l[i].k*mid1+l[i].b));
    }
    printf("%.3lf",ans);
}

1038: [ZJOI2008]瞭望塔

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 2658  Solved: 1084
[Submit][Status][Discuss]

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,注意考虑实数误差带来的问题。

时间: 2024-10-13 22:34:27

bzoj千题计划126:bzoj1038: [ZJOI2008]瞭望塔的相关文章

bzoj千题计划125:bzoj1037: [ZJOI2008]生日聚会Party

http://www.lydsy.com/JudgeOnline/problem.php?id=1037 一个区间是否满足 任意连续的一段,男孩与女孩的数目之差不超过k, 取决于男孩与女孩数目之差的最大值是否满足条件 而且若在一个位置 上坐一个男孩, 所有包含这个位置的区间 男孩与女孩数目之差+1,与女孩与男孩数目之差-1 所以我们只需要知道 最大的数目 之差即可 dp[i][j][k][l] 表示前i个位置,坐了j个男孩,男孩与女孩数目之差=k,女孩与男孩数目之差=l 的方案数 k<0 怎么办

bzoj千题计划185:bzoj1260: [CQOI2007]涂色paint

http://www.lydsy.com/JudgeOnline/problem.php?id=1260 区间DP模型 dp[l][r] 表示涂完区间[l,r]所需的最少次数 从小到大们枚举区间[l,r] 如果col[l]==col[r] dp[l][r]=min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1]+1) 否则 dp[l][r]=min(dp[l][k]+dp[k+1][r]) 我还是辣鸡啊~~~~(>_<)~~~~,这种题都不能秒 #include<c

bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len [i] 表示以i结尾的最长不下降子序列的长度 pre_sum[i] 表示对应长度下的方案数 suf_len[i] 表示以i开头的最长不下降子序列长度 suf_sum[i] 表示对应长度下的方案数 若已有了这4个数组 设最长上升子序列长度=mx 那么 如果pre_len[i]+suf_len[i] -

bzoj千题计划304:bzoj3676: [Apio2014]回文串

https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 300001 char ss[N]; int s[N]; int tot=1,last; int fail[N],len

bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include<cstdio> #include<cstring> #include<iostream> #define L 100001 typedef unsigned long long ULL; using namespace std; char s[L+4]; int tot,root

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 用b数组表示 节点第0/1行的最右一列是否连接了右边 来 辅助 节点的合并 查询 对两个点位于矩形的位置分4种情况讨论 两点是否联通,要考虑四种情况 (以两个位置是矩形左上角和右上角为例) 1.直接联通,线段树的节点包含了这种情况,直接判断 2. 3. 4. 后三种情况需要再查询[1,l]和[r,n

bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] 表示 从i号柱子 上把j个盘子移到 g[i][j] 柱子上的步数 初始化:f[0][1]=1,g[0][1] 根据优先级决定 设三根柱子分别为0,1,2 对于每一个f[x][i], 把前i-1个移走,把第i个移走,把前i-1个移回 令y=g[x][i-1],则k=0+1+2-x-y 我们希望 把i-

bzoj千题计划111:bzoj1021: [SHOI2008]Debt 循环的债务

http://www.lydsy.com/JudgeOnline/problem.php?id=1021 如果A收到了B的1张10元,那么A绝对不会把这张10元再给C 因为这样不如B直接给C优 由此可以推出 若A欠B20元,B欠C 30元, 那么A还C20元,B还C10元最优 所以一共只有 A->BC   B->AC  C->AB AB->C  BC->A  AC->B 这6种转移情况 根据输入,我们可以知道三人最终手中有多少钱ea.eb.ec,一共有多少钱sum 设f

bzoj千题计划112:bzoj1022: [SHOI2008]小约翰的游戏John

http://www.lydsy.com/JudgeOnline/problem.php?id=1022 http://www.cnblogs.com/TheRoadToTheGold/p/6744825.html #include<cstdio> #include<iostream> using namespace std; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar();