求凸函数的极值的一般方法是三分

三分的思想大概是这样的:

例如我们要求下凸函数的极值

在区间[L,R]上,

我们定义m1为区间的第一个三等分点

定义m2为区间的第二个三等分点

设函数值为F(x)

则若F(m1)<F(m2),证明解在[L,m2]中

否则解在[m1,R]中

一般三分的写法是迭代,注意控制精度和时间的平衡

UVa 1476

很容易发现一堆二次函数求max之后还是一个凸函数

之后三分即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std; const int maxn=10010;
int T,n;
int a[maxn],b[maxn],c[maxn]; double F(double x){
double ans=a[1]*x*x+b[1]*x+c[1];
for(int i=2;i<=n;++i)ans=max(ans,a[i]*x*x+b[i]*x+c[i]);
return ans;
} int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d%d%d",&a[i],&b[i],&c[i]);
double L=0.0,R=1000.0;
for(int i=1;i<=100;++i){
double m1=L+(R-L)/3,m2=m1+(R-L)/3;
if(F(m1)<F(m2))R=m2;
else L=m1;
}
printf("%.4lf\n",F(L));
}return 0;
}

  

ZOJ 3203

很坑的题目,首先我们先暴力导出公式发现是个凸函数

之后分类讨论影子会不会射到墙上(其实我的代码trick了一下,实在懒得分类讨论了)

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#define eps 1e-6
using namespace std; int T;
double H,h,D; double F(double M){
return h*(H*(D-M)-h*D)/(h*(D-M)-h*D)+(D-M);
//return (h*(H*M-h*D)+M*(h*M-h*D))/(h*M-h*D);
} int main(){
scanf("%d",&T);
while(T--){
scanf("%lf%lf%lf",&H,&h,&D);
double L=0.0,R=min(H,D);
for(int i=1;i<=10000;++i){
double len=(R-L)/3;
double m1=L+len,m2=m1+len;
if(F(m1)>F(m2))R=m2;
else L=m1;
}
printf("%.3lf\n",F(L));
}return 0;
}

  

BZOJ 1857 SCOI 传送带

首先我们设在第一个传送带上走x距离,第二个传送带上走y距离

因为中间走的是欧几里得距离

所以不难发现对于x整体是一堆类二次函数,由于UVa的题目的解决经验

之后我们尝试三分x

然后对于y,现在的问题就转换成了给定一个类二次函数,最小化G(y)

这也是可以三分的

所以我们就可以三分求出F(x)的极值了

第一次提交TLE是因为三分的迭代次数设的太多了

第二次提交WA是因为计算的时候出现了除0错误(并不知道为什么不会RE)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#define eps 1e-8
using namespace std; double L1,L2;
double P,Q,R;
struct Point{
double x,y;
void read(){scanf("%lf%lf",&x,&y);}
Point(double x=0,double y=0):x(x),y(y){}
}A,B,C,D,u,v,nowA,nowD;
typedef Point Vector;
Vector operator -(const Point &A,const Point &B){return Vector(A.x-B.x,A.y-B.y);}
Vector operator +(const Point &A,const Point &B){return Vector(A.x+B.x,A.y+B.y);}
Vector operator *(const Point &A,const double &p){return Vector(A.x*p,A.y*p);}
double Dot(const Point &A,const Point &B){return A.x*B.x+A.y*B.y;}
double Len(const Vector &A){return sqrt(Dot(A,A));} double G(double x){
double t=0.0;
if(fabs(Dot(v,v))>eps)t=sqrt(x*x/Dot(v,v));
nowD=D+v*t;
return Len(nowD-nowA)/R+x/Q;
}
double F(double x){
double t=0.0;
if(fabs(Dot(u,u))>eps)t=sqrt(x*x/(Dot(u,u)));
nowA=A+u*t;
double L=0.0,R=L2;
for(int i=1;i<=200;++i){
double len=(R-L)/3;
double m1=L+len,m2=m1+len;
if(G(m1)<G(m2))R=m2;
else L=m1;
}return G(L)+x/P;
} int main(){
A.read();B.read();u=B-A;L1=Len(u);
C.read();D.read();v=C-D;L2=Len(v);
scanf("%lf%lf%lf",&P,&Q,&R);
double L=0.0,R=L1;
for(int i=1;i<=200;++i){
double len=(R-L)/3;
double m1=L+len,m2=m1+len;
if(F(m1)<F(m2))R=m2;
else L=m1;
}
printf("%.2lf\n",F(L));
return 0;
}

  

POJ 3301

完全想不到的神思路

首先我们注意到平行于坐标轴的最大正方形非常好算

最后的正方形一定是某个平行于坐标轴的正方形旋转了多少的角度之后得到的

所以我们三分这个旋转的角度,就可以得到答案啦

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std; const int maxn=52;
const double pi=acos(-1.0);
int T,n;
struct Point{
double x,y;
void read(){scanf("%lf%lf",&x,&y);}
}A[maxn],B[maxn]; double F(double x){
for(int i=1;i<=n;++i){
B[i].x=A[i].x*cos(x)-A[i].y*sin(x);
B[i].y=A[i].x*sin(x)+A[i].y*cos(x);
}
double mxy=-1e12,mny=1e12,mxx=-1e12,mnx=1e12;
for(int i=1;i<=n;++i){
mxy=max(mxy,B[i].y);
mny=min(mny,B[i].y);
mxx=max(mxx,B[i].x);
mnx=min(mnx,B[i].x);
}
double len=max(mxy-mny,mxx-mnx);
return len*len;
} int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;++i)A[i].read();
double L=0,R=2*pi;
for(int i=1;i<=300;++i){
double len=(R-L)/3;
double m1=L+len,m2=m1+len;
if(F(m1)<F(m2))R=m2;
else L=m1;
}printf("%.2lf\n",F(L));
}return 0;
}

  

总结:感觉三分这个算法需要注意的东西很多的

首先,我们需要对题目建立模型,并发现它是个凸函数(几何画板胡乱证明之)

其次,在写三分的过程中,要注意迭代次数

迭代次数过少会导致精度不够

过多会导致TLE

最后,在计算F(x)也就是函数值的时候要尽量避免精度误差,并且防止除0错误

  

  

三分初练QAQ的更多相关文章

  1. android初练二

    android 之 Activity的启动方式 1.android的显示启动 显示启动一般用于在用自己的活动时进行页面跳转时常常使用到 public class MainActivity extend ...

  2. QT QT程序初练

    //界面编程#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) ...

  3. CSS+DIV布局初练—DIV元素必须成对出现?

    一直做C/S开发的工作,但是很少做和布局相关的工作,往往都是同事将界面设计好,自己填写代码而已,对于B/S的工作,做过,但是很少没有像C/S这么多,界面布局的话,更无从谈起. 日子就这么过,一天一个样 ...

  4. 循环初练 for

    class Program    {        static void Main(string[] args)        {            while (true)           ...

  5. Selenium_WebDriver登录模拟鼠标移动切换窗体等操作练习(cssSelector初练手)_Java

    cssSelector 据说cssSelector比xpath快. 所以,有固定ID属性的页面元素用By.id或者By.cssSelector("#id属性值")来找,有class ...

  6. Golang初练手-多线程网站路径爆破

    以前用Python写过这个工具,前两天看了golang的基础,就想着用这个语言把这个工具重写一遍 先放张图 用法 Example : Buster.exe -u=https://www.baidu.c ...

  7. C++ 类的抽象初练

    /* 某商店经销一种货物,货物的购进和卖出以箱为单位,各箱的重量不一样, 因此商店需要目前库存的总重量. 现在用c++模拟商店货物购进和卖出的情况 */ #include<iostream> ...

  8. Windows API初练手 -- 疯狂写文件代码

    警告:恶作剧软件,慎用!仅供初学者研究代码所用!!! 提示:默认文件创建目录在"D:\test",如果需要使用的话请自行更改目录. 1. Windows API 版本 (调用系统函 ...

  9. Python3 实例

    一直以来,总想写些什么,但不知从何处落笔. 今儿个仓促,也不知道怎么写,就把手里练习过的例子,整理了一下. 希望对初学者有用,都是非常基础的例子,很适合初练. 好了,Follow me. 一.Pyth ...

随机推荐

  1. 私人定制自己的linux小系统

     私人定制自己的linux小系统 一.前言    linux操作系统至1991.10.5号诞生以来,就源其开源性和自由性得到了很多技术大牛的青睐,每个linux爱好者都为其贡献了自己的一份力,不管是在 ...

  2. CSS3属性box-shadow使用教程

    CSS3的box-shadow属性可以让我们轻松实现图层阴影效果.我们来实战详解一下这个属性. 1. box-shadow属性的浏览器兼容性先来看一个这个属性的浏览器兼容性: Opera: 不知道是从 ...

  3. 如何判断PHP 是ts还是nts版的

    通过phpinfo(); 查看其中的 Thread Safety 项,这个项目就是查看是否是线程安全,如果是:enabled,一般来说应该是ts版,否则是nts版.

  4. 如何使用js捕获css3动画

    如何使用js捕获css3动画 css3动画功能强大,但是不像js,没有逐帧控制,但是可以通过js事件来确定任何动画的状态. 下面是一段css3动画代码: #anim.enable{ -webkit-a ...

  5. 【面试虐菜】—— JAVA面试题(3)

    1 throws与throw的区别 解析:throws和throw是异常处理时两个常见的关键字,初级程序员常常容易正确理解throw和throws的作用和区别,说明已经能比较深入理解异常处理.Thro ...

  6. Word 使用技巧

    文档的写作,例来分为latex与word两大阵营.一个是论文界的宠儿,一个是平民的所见即所得.看起来好像前者更加牛一些. 本来我也是觉得latex比word好.但是使用latex时苦于找不到一个好的编 ...

  7. nginx服务器绑定域名和设置根目录

    首先进入nginx安装目录的配置目录conf,然后执行 vi conf/nginx.conf 打开nginx的配置文件,找到并修改红字部分 server { listen default_server ...

  8. WIN7右下角的声音图标不见了

    有时候电脑启动了,但是声音图标却不见了,造成调试声音相当的麻烦,那么怎么来处理呢? 一:ctrl+shit+Esc键打开任务管理器 二:找到exeplore.exe,结束进程. 三:重新建立进程 上述 ...

  9. IOS开发之──应用之间调用(2)

    在上一篇文章中,讲解了如何在自己应用之间调用问题,今天介绍一下如果调用IOS自带的app的方法 一.调用app store界面方法 在实际开发中,往往要推荐自己其他应用和推荐自己的收费软件,那么我们就 ...

  10. 敏捷开发之道(四)Scrum概述

    上次的博文敏捷开发之道(二)极限编程XP和敏捷开发之道(三)极限编程XP续中,我们介绍了一下敏捷开发中的XP开发方法,今天咱们来了解另一个比较流行的敏捷开发方法--Scrum. 1.Scrum简介 S ...