二分与三分(精度类型)

二分:传送门

三分:传送门

(注意,是五舍六入,不是四舍五入,在2018年10月23日前是这样的)

话说一本通上不是有讲嘛,做法自己看吧。。。(但是我太弱了,精度版看不懂QWQ)。

简单讲一下二分与三分吧。

二分:必须满足单调性:

非增或非减就叫单调性(如果就好几个数相同,一般会用二分来找第一个数或最后一个数)。

我们用两个数字l与r来代表搜索范围,而mid代表中间的位置的值,来跳来跳去,看情况来写。

这题

int  l=1,r=n,mid,ans=0;
while(l<=r)
{
    mid=(l+r)/2;
    if(a[mid]<=m)l=mid+1,ans=mid;
    else  if(a[mid]>m)r=mid-1;
}
printf("%d\n",ans);

当然,还可以用二分来二分答案,比如你要用某种方法,但是缺少一种条件,你又意外发现这个条件越大或越小,会让你方法越容易达到目标(也就是发现了二分性),就可以二分这个条件,不断丢给这个方法,让他运行。

这题

//发现这道题如果二分总和,总和越大,分的段数就越少,越容易小于等于m,也就越容易达到目标,因此得出做法
#include<cstdio>
#include<cstring>
using  namespace  std;
int  a[210000],b[210000],n,m;
bool  pd(int  x)
{
    int  kk=1/*自行理解*/,ans=0;
    for(int  i=1;i<=n;i++)
    {
        if(a[i]>x)return  false;//如果有一个数超过了,就退出
        if(ans+a[i]<=x)ans+=a[i];//加上
        else
        {
            kk++;/*统计答案*/if(kk>m)return  false;//分的段数过多,退出
            ans=a[i];//重置
        }
    }
    return  true;
}
int  main()
{
    int  sum=0;
    scanf("%d%d",&n,&m);
    for(int  i=1;i<=n;i++)scanf("%d",&a[i]),sum+=a[i];
    int  l=1/*这里可以改成a数组的最大值)*/,r=sum/*统计所有的和*/,mid,x;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(pd(mid)==true)//如果可以,代表可以让r再收拢一点
        {
            r=mid-1;x=mid;
        }
        else  l=mid+1;//不行,则扩宽l的限制
    }
    printf("%d\n",x);
    return  0;
}

(我不是标题狗)

但是开头的那道二分题,是要用精度的!!!

看别人的代码,也都是大把大把的double,丑陋的代码:

#include<cstdio>
#include<cstring>
using  namespace  std;
double  a[210000],sum[210000],b[210000];
int  n,L;
double  mymin(double  x,double  y){return  x<y?x:y;}
double  mymax(double  x,double  y){return  x>y?x:y;}
bool  check(double  x)//这个上网搜搜都是有的
{
    double  min_val=999999999.0,ans=-99999999.0;
    for(int  i=1;i<=n;i++)b[i]=a[i]-x,sum[i]=b[i]+sum[i-1];
    for(int  i=L;i<=n;i++)//用DP搜索一段长度大于等于L的子串的最大和
    {
        min_val=mymin(min_val,sum[i-L]);
        ans=mymax(ans,sum[i]-min_val);
    }
    return  ans>=0.0;//猴子已经死机。。。上网搜吧。。。
}
int  main()
{
    scanf("%d%d",&n,&L);
    for(int  i=1;i<=n;i++)scanf("%lf",&a[i]);
    double  l=-1e6,r=1e6,mid,ans=0.0,jie=1e-5;//猴子已死机。。。
    while(r-l>jie)
    {
        mid=(l+r)/2;
        if(check(mid))ans=mid,l=mid;
        else  r=mid;
    }
    printf("%d\n",int(r*1000.0));
    return  0;
}

---



还是上网弄了一个下来

这题可以用斜率优化做也可以用二分做,我用的是二分做法。

题意:给你n个牛的自身价值,让你找出连续的且数量大于等于F的一段区间,使这段区间内的牛的平均价值最大。

思路:用二分枚举平均值ave,每个牛的价值都减去ave,看是否有连续的超过f长度的区间使得这段区间的价值大于等于0,如果能找到,那么说明这个平均值可以达到。先每个a[i]减去ave得到b[i],用dp[i]表示以i为结尾区间连续长度大于等于f的最大连续区间和,maxx[i]表示以i为结尾的最大连续区间和,sum[i]表示1~i的价值总和那么maxx[i]=max(maxx[i-1]+b[i],b[i]),dp[i]=maxx[i-f+1]+sum[i]-sum[i-f+1],判断是否有一个i(i>=f)满足dp[i]>=0.

作者:Herumw

来源:CSDN

原文:https://blog.csdn.net/kirito_acmer/article/details/48716719

版权声明:本文为博主原创文章,转载请附上博文链接!

------

---

如果你是神犇看懂了=_=。哥哥,我们不约

好的,如果你不是神犇,那么请:

不过,有时,我们不一定要用double,我们乘以一个1000转成int,然后在check再暂时转回double

代码(摘自我机房大佬CLB的代码):

#include<cstdio>
#include<cstring>
using  namespace  std;
double  a[210000],sum[210000],b[210000];
int  n,L;
double  mymin(double  x,double  y){return  x<y?x:y;}
double  mymax(double  x,double  y){return  x>y?x:y;}
bool  check(double  x)
{
    double  min_val=999999999.0,ans=-99999999.0;
    for(int  i=1;i<=n;i++)b[i]=a[i]-x,sum[i]=b[i]+sum[i-1];
    for(int  i=L;i<=n;i++)
    {
        min_val=mymin(min_val,sum[i-L]);
        ans=mymax(ans,sum[i]-min_val);
    }
    return  ans>=0.0;
}
int  main()
{
    scanf("%d%d",&n,&L);
    for(int  i=1;i<=n;i++)scanf("%lf",&a[i]);
    double  l=-1e6,r=1e6,mid,ans=0.0,jie=1e-5;
    while(r-l>jie)
    {
        mid=(l+r)/2;
        if(check(mid))ans=mid,l=mid;
        else  r=mid;
    }
    printf("%d\n",int(r*1000.0));
    return  0;
}

到三分了,三分呢,主要解决单峰问题(求单峰),不过递增或递减也可以哟不过只是求第一个数或最后一个数

所谓三分,肯定是把区间用两个数(记作m1与m2,m1<=m2)分成三个部分(除了某些特殊情况:n=2...),然后将这两个数比较,然后l跳到m1+1,r跳到m2-1,然后当l>r退出,由于这里比二分还复杂,所以猴子还没找到直接记录答案的方法,只能最后比较l与r,虽然一次只缩小\[1/3\]

但是总比某退火的玄学复杂度快吧。

举个栗子(二次函数:开口向上):

以x轴做三分,那么m1(A点)的y小于m2(B点)的y,那么我们就让r跳到比m2小一点的地方(按题目来定),反之让l跳到比m1大一点点的位置,来达到我们将l与r缩小的目的。

至于突然退化成一次函数的二次函数(某毒瘤出题人干的),与二次函数的情况一样,不过l或r有一个不变罢了。。。

伪例题:

一个序列,其中有一个数,这个数左边的序列严格递增,左边严格递减,右边严格递增。

一个整数n

n个数字

输出这个数字:

样例输入:

5

1 2 3 2 1

样例输出:

3

int  l=1,r=n,m1,m2;
while(l<=r)
{
    m1=l+(r-l+1/*+1不+1都可以*/)/3/*为什么不+1?如果l==r,m1就跳出去了,m2也是同理*/;m2=r-(r-l+1)/3;
    if(a[m1]<=a[m2]/*<与<=都可以*/)r=m2-1;
    else  l=m1+1;//缩小范围
}
if(a[l]<a[r])printf("%d\n",a[l]);
else  printf("%d\n",a[r]);

至于如果有一段的值相等,这种情况我认为是可以的,欢迎大家再我的下方评论,毕竟三分刚学不久。。。

比如上图。。。

然后,又到了开头的那道三分了。。。

又是精度问题!

至于单峰性。。。



题目:给你n条开口向上的二次曲线Si(a>0),定义F(x) = max(Si(x)),求F(x)的最小值。

分析:三分。F(x)是一个单峰函数,先单调递减后单调递增,利用三分求最小值。

        首先,证明两个二次函数构造的F2(x)为单峰函数;

        (如果不成立,则存在两个连续的波谷,交点处一个函数递增另一个递减,矛盾,不会取递减函数)

        然后,用数学归纳法证明,Fi(x)为单峰函数,则Fi+1 = max(Fi(x),Si+1(x))也是单峰函数;

        (如果存在两个(或更多)连续的波谷,交点处一个函数递增另一个递减,矛盾,所以只有一个波谷)

        结论,综上所述得证结论,只存在一个波谷。


作者:小白菜又菜

来源:CSDN

原文:https://blog.csdn.net/mobius_strip/article/details/45618095

版权声明:本文为博主原创文章,转载请附上博文链接!



看不懂自己YY吧,啊啊啊啊。

难道又要动用我们毒瘤可爱的double了?

不,我拒绝!!!

既然保留四位小数,又五舍六入,那么乘100000啦!

#include<cstdio>
#include<cstring>
using  namespace  std;
typedef  long  long  ll;
ll  a[200000],b[200000],c[200000],n;
inline  double  mymax(double  x,double  y){return  x>y?x:y;}
inline  double  cai(ll  x)//从所有函数中选最大值
{
    double  xx=x/100000.0;//变回double
    double  mmax=-999999999;
    for(ll  i=1;i<=n;i++)mmax=mymax(mmax,(a[i]*1.0)*xx*xx+(b[i]*1.0)*xx+(c[i]*1.0));
    return  mmax;
}
int  main()
{
    ll  T;scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld",&n);
        for(int  i=1;i<=n;i++)scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        ll  l=0,r=1e8/*1*10的8
        次方*/,m1,m2,ans;//三分日常操作
        while(l<=r)
        {
            m1=l+(r-l+1)/3;m2=r-(r-l+1)/3;
            if(cai(m1)<=cai(m2))r=m2-1;
            else  l=m1+1;
        }
        if(cai(l)<cai(r))printf("%.4lf\n",cai(l));
        else  printf("%.4lf\n",cai(r));//统计答案
    }
    return  0;
}

。。。

作者可能学了个假的三分。。。

不过,如果乘以一百万(多乘了个10)。。。

猴子(作者)想了一个坏想法,于是我用了高精度1e10与1e11,终于,在1e11时卡精度AC了!

AC代码:

//猴子将double*1e8将他转为long  long
#include<cstdio>
#include<cstring>
using  namespace  std;
typedef  long  long  ll;
ll  a[200000],b[200000],c[200000],n;
inline  double  mymax(double  x,double  y){return  x>y?x:y;}
inline  double  cai(ll  x)//转回long long;
{
    double  xx=x/100000000.0;
    double  mmax=-999999999;
    for(ll  i=1;i<=n;i++)mmax=mymax(mmax,(a[i]*1.0)*xx*xx+(b[i]*1.0)*xx+(c[i]*1.0));
    return  mmax;
}
int  main()
{
    ll  T;scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld",&n);
        for(int  i=1;i<=n;i++)scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        ll  l=0,r=1e11,m1,m2,ans;
        while(l<=r)
        {
            m1=l+(r-l+1)/3;m2=r-(r-l+1)/3;
            if(cai(m1)<cai(m2))r=m2-1;
            else  l=m1+1;//三分
        }
        if(cai(l)<cai(r))printf("%.4lf\n",cai(l));
        else  printf("%.4lf\n",cai(r));
    }
    return  0;
}

所以,带精度的二分与三分是可以转成long long来做的,在check里再转会double就行了,不过三分可能要多乘一点。

终于写完了。

每日笑话:

追到我的女神 我用了三个办法 办法一 坚持 办法二 不要脸 办法三 坚持不要脸 她带我回家 她爸爸很无礼地跟我说 我养了我女儿二十年 我凭什么把她嫁给你 我回答 你养她二十年 我要养她四十年 还要照顾你三十年 你凭什么不把她嫁给我

--------来源

--------来源

感谢大家观看。

原文地址:https://www.cnblogs.com/zhangjianjunab/p/9841479.html

时间: 2024-10-11 20:55:01

二分与三分(精度类型)的相关文章

ACM之路(1)——对二分及三分的研究

对于二分来说,个人暂且将其分为整数型和实数型.      对整数型而言,一般问题对于(low<=high)条件的判断取等号即可,因为在while途中查找遇到可匹配的便break,比方说二分查找已排序的数列.但是有些问题并不是这样的,在此分为两类:取最大值的min,和取最小值的max.      对于前一种,要用<(即不断往下推得最优解),而后一种要用<=(即不断往上推得最优解).同时,若对于前一种情况而言,因为要取最小的最优解,那么即使取中的mid可以满足条件仍要将high=mid-1以

分治 二分答案 三分未完结

分治,字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题--直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并.分治法是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)等等. 分治三步法: 1 划分问题:把问题划分成元素个数尽量相等的两半: 2 递归求解:把两半元素分别求解: 3 合并问题:把两个已经求解的元素合并成一个: 二分的基本用途是在单调序列或单调函数中做查找操作,注意:二

二分与三分

(写的很早忘了发布了...)今天高二的学长学姐们进行了二分和三分的测试(又名一本通课后题测试),感觉这个好有意思~ 关于什么是二分,三分,先咕咕咕了,来看一道题. 灯泡:https://loj.ac/problem/10016 题意概述:给出$H,h,D$,求阴影的最长长度(包括地上的和墙上的). 有种初中数学的既视感. 解:设灯泡为$A$,人头为$B$,连接$AB$并延长,与地面的延长线交于点$C$. 于是就结束了...吗? 还有一种可能就是影子并没有到墙上去,但是影响不大,两个函数叠加起来还

【信息学奥赛一本通】Part1.2 基础算法-二分与三分

问题 A: [二分和三分]愤怒的牛 题目描述 农夫约翰建造了一座有n间牛舍的小屋,牛舍排在一条直线上,第i间牛舍在xi的位置,但是约翰的m头牛对小屋很不满意,因此经常互相攻击.约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其它牛尽可能远的牛舍.也就是要最大化最近的两头牛之间的距离. 牛们并不喜欢这种布局,而且几头牛放在一个隔间里,它们就要发生争斗.为了不让牛互相伤害.John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是多少呢? 输入 第一行用空格分

《算法竞赛进阶指南》学习总结 二分与三分

首先......我是一个很菜很菜的萌新,所以这篇文章写得很详细,有很多我自己的口水话方便我理解,请各位谨慎食用qwq 以前在网上找过很多介绍二分的博客,但都感觉对萌新不太友好,反正我当时连跳石头都没看懂,所以决定自己写一篇!其中有我的想法,也借鉴了书里的很多内容,感谢lyd. 二分答案,顾名思义,就是对我们所需要的答案进行二分,对我们要求的值进行二分.二分的基础用法是在单调序列或者单调函数当中查找,当答案具有单调性,我们就可以采用二分来计算,当然还有三分,在后面我会详细讲到 整数集合上的二分 在

ACM : HDU 2899 Strange fuction 解题报告 -二分、三分

Strange fuction Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5933 Accepted Submission(s): 4194 Problem Description Now, here is a fuction: F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100) C

hdu 2899 凸性 二分 / 三分

Strange fuction Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 7230    Accepted Submission(s): 4996 Problem Description Now, here is a fuction:  F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=

Cable master(二分题 注意精度)

Cable master Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 26596   Accepted: 5673 Description Inhabitants of the Wonderland have decided to hold a regional programming contest. The Judging Committee has volunteered and has promised to

二分和三分

二分: 二分,即为折半查找,它是一种效率较高的查找方法.但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列. 也就是说,二分的条件为必须满足数列或某一逻辑的顺序性,(单调 只有这样才能进行二分. 查找方法: 首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功:否则利用中间位置记录将表分成前.后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表.重复以上过程,直到找到满足条件的记录,