题解 yzoj1663: 愤怒的牛(二分) yzoj1662: 曲线(三分)

话说二分和三分的题还没有整理过,就趁这两题来整理下笔记

先讲讲关于二分,对于二分的具体边界长期以来对我来说都是个玄学问题,都是边调边拍改对的。思路大体是确定左边界l,和有边界r,判断满足条件缩小范围。

放个大概的代码

while(l+ep<r){
      lm=l+(r-l)/3.0;
      rm=r-(r-l)/3.0;
      if(clu(lm)>clu(rm)) l=lm;
      else r=rm;
}

二分用处很大,一般用在二分答案以及二分查找,一般看到最大的最小或最小的最大都是二分答案或二分查找题,一般来说二分答案题的套路都大体一致。

二分答案:luogu P1182,P2678

二分查找:luogu P1496(离散化+二分查找)

关于三分,大概就是在二分的基础上,对左右区间再进行一次二分,三分查找一般用来确定单峰函数的最值

%E6%89%B9%E6%B3%A8%202019-08-03%20233258.png

于二分类似先取中间值

mid=(l+r)>>1

再取mid于Right的中间值

rmid=(mid+r)>>1

通过f(mid)于f(rmid)的值来缩小范围

当然还有另外一种写法

1.先把整个区间的n/3的值lmid←n/3+left。

2.再取右侧区间的中间值rmid←right-n/3,从而把区间分为三个小区间。

3.用f(lmid)的值与f(rmid)的值来缩小范围

   double ep=1e-9;
   while(l+ep<r){//使用ep来控制精度
      lm=l+(r-l)/3.0;
      rm=r-(r-l)/3.0;
      if(clu(lm)>clu(rm)) l=lm;
      else r=rm;
   }

这样的时间复杂度是O(lon3n)

回到题目

1.愤怒的牛

题意:将c头牛放入相隔距离不同的n个牛舍中,要求任意两头牛相隔最小距离最大

对于这道题,我们可以二分查找,注意细节即可

#include<bits/stdc++.h>
using namespace std;
int n,l,r,c,a[100010];
int main(){
    scanf("%d %d",&n,&c);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    l=0,r=a[n];
    while(l<=r){
        int mid=(l+r)>>1;
        int cnt=1;//初始值为1,把第一头牛放到一号牛舍一定最优
        int tmp=a[1];
        for(int i=1;i<=n;++i){
            if(a[i]-tmp>=mid){
                cnt++;
                tmp=a[i];
            }
        }
        if(cnt<c) r=mid-1;
        else l=mid+1;
    }
    printf("%d",r);
    return 0;
} 

2.曲线

题意:n个二次函数,第i个二次函数g(x)=aix^2+bix+ci( \(i \epsilon [1, n]\) )(二次函数可能退化为一次函数),f(x)=max(g(x))(n个二次函数中的最大值)
求f(x)在 \(x \epsilon [0, 1000]\) 的最小值,易证f(x)为单峰函数,三分查找即可

#include<bits/stdc++.h>
using namespace std;
int T,n;
double ep=1e-9;//控制精度
double a[10010],b[10010],c[10010];
double clu(double x){
    double sum=0;
    for(int i=1;i<=n;++i) sum=max(sum,((a[i]*x)+b[i])*x+c[i]);//展开即为g(x)
    return sum;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%lf %lf %lf",&a[i],&b[i],&c[i]);
        }
        double l=0,r=1000,lm=0,rm=0;
        while(l+ep<r){
            lm=l+(r-l)/3.0;
            rm=r-(r-l)/3.0;
            if(clu(lm)>clu(rm)) l=lm;
            else r=rm;
        }
        printf("%.4lf\n",clu(l));
    }
    return 0;
} 

转载于:https://www.cnblogs.com/donkey2603089141/p/11414852.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值