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

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

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

在区间[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. Android AdapterView.OnItemSelectedListener

    AdapterView.OnItemSelectedListener Summary Public Methods abstract void onItemSelected(AdapterView&l ...

  2. SQL 面试题(一)

    问题来自于CSDN问答,练练SQL吧. 测试数据SQL代码: if OBJECT_ID('td_ls_2') is not null drop table td_ls_2 go if OBJECT_I ...

  3. JavaScript 中的 replace 方法

    定义和用法 replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串. stringObject.replace(regexp/substr,replaceme ...

  4. Ubuntu下设置Tomcat成为服务(开机启动)

    1.将tomcat安装目录下bin文件夹中的catalina.sh拷贝到/etc/init.d下并修改名称为tomcat cp  /path/to/tomcat/bin/catalina.sh /et ...

  5. 精美舒适的对话消息提示框--第三方开源--SweetAlertDialog

    SweetAlertDialog(sweet-alert-dialog)是一个套制作精美.动画效果出色生动的Android对话.消息提示框 SweetAlertDialog(sweet-alert-d ...

  6. linux前景到底怎么样啊?

    我就不长篇大论,举四个例子你看看. 1.目下最热最潮最流行的云计算技术的背后是虚拟化和网格技术,而虚拟化和网格技术基本是Linux的天下,目前虚拟化的三大家:Vmware,Xen,Hyper-V中,市 ...

  7. mac常用设置

    1.修改mac主机名 系统偏好设置->共享->电脑名称 ,编辑就可以了. sudo scutil --set HostName hostname 这个是修改主机名 sudo scutil ...

  8. CentOS thrift python demo

    编辑接口文件 hellowworld.thrift service HelloWorld { string ping(), string say(1:string msg) } 编辑 server.p ...

  9. Python之str(),repr(),``

    对于对象obj: str()生成的字串是给人看的 repr()生成的字串是给解析器看的 ``与repr()等义. 最直接就是: ------------------- obj=eval(repr(ob ...

  10. IOS键盘样式风格有关设置

    一.键盘风格 UIKit框架支持8种风格键盘. typedef  enum  { UIKeyboardTypeDefault,                 // 默认键盘:支持所有字符 UIKey ...