zoukankan      html  css  js  c++  java
  • 三分查找模板和例题

    通过左右三分定出两个点,判断两点的高低,然后舍掉三分之一的距离,寻找凸函数的极值点。

    • 如果 lmid 和 rmid 在最大(小)值的同一侧: 那么由于单调性,一定是二者中较大(小)的那个离最值近一些,较远的那个点对应的区间不可能包含最值,所以可以舍弃。
    • 如果在两侧: 由于最值在二者中间,我们舍弃两侧的一个区间后,也不会影响最值,所以可以舍弃。

    例题1:P3382 【模板】三分法

    题意:三分模板题,求n次函数在[l,r]的极值点,保留5位小数,保证答案存在。

    (这道题的题解很有趣,什么鬼都出来了,明明是三分模板,偏偏有很多大佬要凑热闹,粒子群算法、求导+二分、模拟退火算法、黄金分割法、梯度下降、四分等,留下了不学无术的泪水)

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<math.h>
    #include<string>
    #include<map>
    #include<queue>
    #include<stack>
    #include<set>
    #include<ctime>
    #define ll long long
    #define inf 0x3f3f3f3f
    const double pi=3.1415926;
    using namespace std;
    
    double a[15];
    int n;
    double l,r;
    
    double cal(double x){///计算n次函数在x点的值
        double res=0;
        for(int i=n;i>=0;i--)
            res+=a[i]*pow(x, i);
        return res;
    }
    
    int main()
    {
        scanf("%d%lf%lf",&n,&l,&r);
        for(int i=n;i>=0;i--)
            scanf("%lf",&a[i]);
        double eps=0.0000001;///减小误差
        while(fabs(r-l)>=eps){
            double m=(r-l)/3;///三分距离
            double lm=l+m;///左三分点
            double rm=r-m;///右三分点
            if(cal(lm)<cal(rm))
                l=lm;
            else
                r=rm;
        }
        printf("%.5f
    ",l);
        return 0;
    }

    另外,不知道为什么Java的代码全部超时,代码复制得一模一样,请指教。

    import java.util.*;
    
    public class Main{
        
        static double[] a=new double[15];
        static int n;
        static double eps=0.0000001;
        public static void main(String[] args) {
            Scanner scan=new Scanner(System.in);
            n=scan.nextInt();
            double l=scan.nextDouble();
            double r=scan.nextDouble();
            for(int i=n;i>=0;i--) 
                a[i]=scan.nextDouble();
            double ans=0;
            while(Math.abs(r-l)>eps) {
                double m=(r-l)/3;
                double lm=l+m;;
                double rm=r-m;;
                if(cal(lm)<=cal(rm))
                    l=lm;
                else 
                    r=rm;
            }
            System.out.printf("%.5f
    ",l);
        }
        
        public static double cal(double x) {
            double res=0;
            for(int i=n;i>=0;i--) 
                res+=a[i]*Math.pow(x, i);
            return res;
        }
    }
    玄学T

    例题2:牛客练习赛59C装备合成

    题意:有x件材料a和y件材料b,2件材料a和3件材料b可以做一件装备A,4件材料a和1件材料b可以做一件装备B,求最多能做多少装备。1<=a,b<=1e9

    思路:假设装备A做了a件,装备B则是min( (x-2a)/4,y-3a ),答案ans=a+min( (x-2a)/4,y-3a )。大佬们说:不难看出是凸函数,然后三分查找。

    赛后补题的,题解说是三分那就是三分,大佬说打表就能看出是三分,可能是题做多了就有经验吧,代码附带打表函数可以像大佬们一样测试。

    偶然发现的坑:三分三分,像我一样的初学者设置while条件就喜欢设成r-l<=3,刚好可以被3除,皆大欢喜。但是对于整数,要考虑取整问题,如果这样设while条件,在if判断中加一个等号和不加等号会导致结果不一样,可以修改代码测试一下,详看代码。为了保证准确性,最终出来的l和r最好是一段稍微大一点的区间,再去遍历求极值,稳!

    import java.util.*;
    
    public class Main{//牛客练习59C
        static int x,y;
        public static void main(String []args) {    
            Scanner scan=new Scanner(System.in);
            /* 大佬们说的打表,还真是这么回事
            dabiao(300, 300);
            dabiao(60,10);
            */
            int t=scan.nextInt();
            while(t!=0) {
                t--;
                x=scan.nextInt();
                y=scan.nextInt();
                int l=0,r=Math.min(x/2, y/3);
                while(r-l>=9) {//这里的差值不要用3,用稍微大一点的区间
                    int m=(r-l)/3;
                    int lm=l+m;
                    int rm=r-m;
                    //System.out.printf("lm=%d cal(lm)=%d   rm=%d cal(rm)=%d
    ",lm,cal(lm),rm,cal(rm));
                    if(cal(lm)<cal(rm))//把while设成r-l>=3后加不加等号第四组数据会出错。
                        l=lm;
                    else 
                        r=rm;
                }
                //System.out.printf("l=%d r=%d
    ",l,r);
                int ans=0;
                for(int i=l;i<=r;i++) //上面只是锁定了一个区间,没能找到最值,左右区间不超过3,再遍历一下
                    ans=Math.max(ans, cal(i));
                System.out.println(ans);
            }
        }
        public static int cal(int a) {
            int res=a+Math.min( (x-2*a)/4, y-3*a);
            return res;
        }
        
        public static void dabiao(int x,int y) {//大佬说的打表
            //从做0件A装备到全部做A装备
            System.out.printf("打表x=%d y=%d
    ",x,y);
            for(int i=0;i<Math.min(x/2, y/3);i++) {
                int b=Math.min( (x-2*i)/4, y-3*i);
                System.out.printf("a=%d b=%d sum=%d
    ",i,b,i+b);
            }
            System.out.println();
            
        }
        
    }
  • 相关阅读:
    shell脚本根据端口号kill掉进程
    使用netstat -ano 查看机器端口的占用情况(windows环境)
    分享一两个小工具,
    将压缩文件伪装图片格式文件以及将python文件转化为exe文件(测试完,真的有效)
    celery 异步任务 周期任务 定时任务的实现
    wsgi、uwsgi、asgi协议的关系
    centos7忘记密码更改步骤
    工作遇到的坑以及自己的学习悟道之道
    案例小集锦
    asp.net mvc部署
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/12504280.html
Copyright © 2011-2022 走看看