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

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

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

在区间[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. VIM小技巧之文件名补全

    恩,这两天在看<简明Python教程>,那里面作者建议写代码的时候前面的注释写上文件名,写上调用的解释器,比如这样: 恩,然后我当然不可能每回新建一个文件,就要在开头写上一大串东西啊,vi ...

  2. 使用tortoise git管理gitolite版本库

    gitolite-admin是用于管理git版本库的版本库,将其从服务器上clone下来. 使用tortoise git clone的时候需要指定私钥,私钥的格式是ppk的,需要使用putty的PUT ...

  3. Sorl之java操作

    参考教程: http://www.cnblogs.com/xia520pi/p/3625232.html http://www.cnblogs.com/hujunzheng/p/5647896.htm ...

  4. 小课堂Week9 例外处理设计的逆袭Part2

    小课堂Week9 例外处理设计的逆袭Part2 今天继续阅读<例外处理设计的逆袭>这本书,我们先看两个案例: 案例1 问:如果要设计一个依据学号到数据库中查询学生资料的函数,当找不到符合条 ...

  5. openerp学习笔记 错误、警告、提示、确认信息显示

    1.检查业务逻辑中的错误,终止代码执行,显示错误或警告信息: raise osv.except_osv(_('Error!'), _('Error Message.')) 示例代码: #删除当前销售单 ...

  6. Java集合的小抄

    在尽可能短的篇幅里,将所有集合与并发集合的特征.实现方式.性能捋一遍.适合所有"精通Java",其实还不那么自信的人阅读. [转自:花钱的年华] 期望能不止用于面试时,平时选择数据 ...

  7. Rac & DG

    Rac环境: RAC版本异同:[10R2,11R1(和10类似)],11R2,12c: 目录: 10.2的ASM需要单独的目录(oracle home):rdbms home,asm home, cr ...

  8. iOS的SandBox的结构研究

    在模拟器中运行iOS程序,都会为该程序创建一个沙盒(SandBox).首先声明,我用的系统是Max OS X 10.7.3,编译器是Xcode 4.3.2.想要找到沙盒目录,先运行Finder,然后在 ...

  9. SharePoint 2010/SharePoint 2013 Custom Action: 基于Site Collection 滚动文字的通知.

    应用场景: 有时候我们的站点需要在每个页面实现滚动文字的通知,怎么在不修改Master Page的情况下实现这个功能?我们可以使用Javascript 和 Custom Action 来实现. 创建一 ...

  10. Eclipse启动的时候窗口一闪就关的解决办法(转)

    有时候会碰到如题这种问题,从网上查知解决办法,非常管用 为eclipse.exe创建一个快捷方式,然后快捷方式上右键-属性,在目标栏填入 E:\eclipse\eclipse.exe -vm &quo ...