ACM新手DAY 8 二分搜索

题解

A - Can you find it?

题目:三个数组A、B、C,一个整数k,三个数组各取一个数相加,判断有没有可能组成k。

  • 二分
  • 用三个循环自然会超时,所以把前两个数组两两相加,对第三个使用二分法。
  • Memory Limit Exceeded:数组开大的话是有可能出现这个报错的
  • 二分法部分
for(int i=0; i<n; i++)
            {
                int l=0, r=k-1;
                while(l<=r)
                {
                    int mid=(l+r)/2;
                    if(D[mid]+C[i]==S[p])
                    { ans=true; break; }
                    else if(D[mid]+C[i]<S[p])
                    { l=mid+1; }
                    else r=mid-1;
                }
                if(ans) break;
            }

B - Trick or Treat

题目:一个笛卡尔坐标系中有n间房子(对应n个坐标点),n个孩子一人对应一个房子,同时从房子向集合点赶去。集合点的纵坐标为0,找到合适的集合点使集合花费的时间最短,并求出花费的时间是多少。

  • 三分(三分模板有两种,这里用的是第二种模板1
  • 分析:花费时间多少自然由离得最远的那个人决定,目的就是使离集合点最近的人的路程最短。题目可以简化为两个点,当集合点在最左边,时间由右边最远的那个点决定,集合点向右移动的过程中,最远距离在减小,之后到了一个临界又会增加,直至到了最右边,最远距离成了最左边那个点,整个过程就是一个凸函数变化,所以选用三分法。
  • 注意点:cin、cout会超时的;不是多组输入。
  • 找最长距离的函数
double f(double a)//这个函数用来找到选定一个点时最长的那一段
{
    double s(0), mm;
    for (int i = 1; i <= n; i++)
    {
        mm = sqrt((a - x[i])*(a - x[i]) + y[i] * y[i]);
        if (s < mm) s = mm;
    }
    return s;
}
  • 搜索部分
while (l + eps < r)//一个网上搜来的三分搜索的模板。可能是哪里用错了,学长讲的那个一直过不了样例
        {
            mid1 = (l + r) / 2.0;
            mid2 = (mid1 + r) / 2.0;
            if (f(mid1) < f(mid2)) r = mid2;
            else l = mid1;
        }
        printf("%.6f %.6f", mid1, f(mid1));

G - UmBasketella

题目:给出一个圆锥体的表面积求它的体积最大可能是多少。同时给出高和底面半径。

  • 三分或数学思维
  • 分析:1.人力计算。可以通过 S = π r l + π r r l l = r r + h h v = 1 / 3 π r r h S=π*r*l+π*r*r,l*l=r*r +h*h,v=1/3*π*r*r*h, 列出一个只和 v r v,r, 有关的方程,求出最大值的代数式。。。2.三分。搜索半径。
  • 三分法
#include <iostream>
#include <cmath>
#include <iomanip>
#define eps 1e-10
#define PI acos(-1.0)
using namespace std;
double s;
double Volume(double r)
{
    double L  = s/r/PI - r; //S = PI * r * (r+L)
    double h  = sqrt(L*L - r*r); //L * L =  h*h + r*r
    return PI*h*r*r/3 ; //V = PI * h * r*r *(1/3)
}
int main()
{
    while(cin >> s)
    {
        double left = 0;
        double right= sqrt( s / PI );
        while(right- left > eps)//三分搜索半径
        {
            double mid1 = left + (right- left) / 3;
            double mid2 = right- (right- left) / 3;
            double v1 = Volume(mid1);
            double v2 = Volume(mid2);
            if(v1<v2)
                left = mid1;
            else right= mid2;
        }
        double ansL = s/left/PI - left;
        double ansh = sqrt(ansL*ansL - left*left);
        double ansv = PI*ansh*left*left/3 ;
        cout << fixed << setprecision(2) << ansv << endl << ansh  << endl << left << endl;
    }
    return 0;
}
  • 数学
#include<iostream>
#include<cmath>
#include<iomanip>
#define PI acos(-1.0)//PI的定义
using namespace std;
int main()
{
    double S;
    while(cin>>S)
    {
        double r=sqrt(S/4/PI);
        double V=S*sqrt(S/(8*PI))/3;
        double h=3*V/PI/(r*r);
        cout << fixed << setprecision(2) << V << endl << h << endl << r <<endl;
    }
    return 0;
}
//直接用函数求最值,先得出r=sqrt(s/4/pi),然后v=sqrt(s*s*s/8/pi)/3,然后h直接用底乘高乘三分之一
  • 今日小结:1、二分法查找是一种在有序数组中查找特定元素的搜索算法。如今天的A题,二分查找的时间复杂度O(log2n)。就单调性的数组来说,二分法应该是够用的而且便于分析。当遇到有凹凸性的,二分法不再适用,就有了三分法。
    2、三分法适用于凸凹函数变化类型的。
    19/7/28 更新,关于“对于一个有序数组的查找,二分法同比三分,四分,乃至五分哪个更好,为什么?”的问题,在zhihu上得到了大佬(好地方bug)的回答:

在这里插入图片描述

  • 上周小结:可以分辨出更多类型的题目了,也学到了几个模板。可能还没有找好学习的节奏,掌握不深。新的头文件及其对用的函数,新的思想,新的套路,样例的坑,小提示的坑,输出的坑,奇怪的报错类型…具体像freopen("input.txt", "r", stdin);freopen("output.txt", "w", stdout); 这样的,还有ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

  1. 三分法有两种理解:
    在区间内用两个mid将区间分成三份,这样的查找算法称为三分查找,也就是三分法,三分法常用于求解单峰函数的最值。
    一种理解是找三分点 ,还有一种理解,即在二分查找的基础上,在左区间或者右区间上再进行一次二分。 ↩︎

发布了47 篇原创文章 · 获赞 4 · 访问量 1314

猜你喜欢

转载自blog.csdn.net/listenhhh/article/details/96892062