• 题解 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(离散化+二分查找)

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

    于二分类似先取中间值

    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
    ",clu(l));
        }
        return 0;
    } 
    
  • 相关阅读:
    【学习笔记/题解】树上启发式合并/CF600E Lomsat gelral
    【学习笔记/题解】虚树/[SDOI2011]消耗战
    【题解】 [GZOI2017]小z玩游戏
    【题解】CF1426E Rock, Paper, Scissors
    【题解】CF1426D Non-zero Segments
    【题解】NOIP2018 填数游戏
    【题解】NOIP2018 旅行
    【题解】NOIP2018 赛道修建
    【题解】时间复杂度
    【题解】「MCOI-02」Convex Hull 凸包
  • 原文地址:https://www.cnblogs.com/donkey2603089141/p/11414852.html
Copyright © 2020-2023  润新知