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

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

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

在区间[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. PHP合并数组保留key值

    PHP合并数组,键值不变   尝试了好几个合并数组的函数, 但是都是把key值重置, 导致key值丢失(因为key值是要用到的) 大大说, 最好用数组的相关函数, 网上随意找了下, 还是没找到. 因为 ...

  2. IntelliJ IDEA+Tomcat+Nginx运行git项目

    1.克隆Git项目到本地 (1)设置Git工具路径:file>settings>Version Control>Git (2)设置GitHub账户:file>settings& ...

  3. $.each遍历json对象

    查看一个简单的jQuery的例子来遍历一个JavaScript数组对象. var json = [ {"id":"1","tagName": ...

  4. 仿UC天气下拉和微信下拉眼睛头部淡入淡出--第三方开源--PullLayout

    Android-PullLayout是github上的一个第三方开源项目,该项目主页是:https://github.com/BlueMor/Android-PullLayout 原作者项目意图实现类 ...

  5. Oracle表空间、段、区和块

    数据块(Block) 数据块Block是Oracle存储数据信息的最小单位.注意,这里说的是Oracle环境下的最小单位.Oracle也就是通过数据块来屏蔽不同操作系统存储结构的差异.无论是Windo ...

  6. Game Tutorials

    SDL: http://www.sdltutorials.com/tutorials        http://lazyfoo.net/ http://panda3d.noie.name/ http ...

  7. PAT乙级真题1008. 数组元素循环右移问题 (20)

    原题: 1008. 数组元素循环右移问题 (20) 时间限制400 ms内存限制65536 kB 一个数组A中存有N(N>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(M&g ...

  8. cocos2dx中的内存管理机制及引用计数

    1.内存管理的两大策略: 谁申请,谁释放原则(类似于,谁污染了内存,最后由谁来清理内存)--------->适用于过程性函数 引用计数原则(创建时,引用数为1,每引用一次,计数加1,调用结束时, ...

  9. mysql存储过程中字符串参数单引号

    注意:存储过程中单引号  ,四个单引号 SET @sql = CONCAT('select user_id into ',m_user_id,' from go_user where mobile = ...

  10. PDF.NET框架操作——工具应用(一)

    PDF.NET是个开源的项目其解决UI层(WinForm / Web)控件数据绑定.映射与查询: BLL层实体对象查询(OQL):DAL层SQL语句和.NET数据访问代码映射(查看  SQL-MAP ...