三分初练QAQ
求凸函数的极值的一般方法是三分
三分的思想大概是这样的:
例如我们要求下凸函数的极值
在区间[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的更多相关文章
- android初练二
android 之 Activity的启动方式 1.android的显示启动 显示启动一般用于在用自己的活动时进行页面跳转时常常使用到 public class MainActivity extend ...
- QT QT程序初练
//界面编程#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) ...
- CSS+DIV布局初练—DIV元素必须成对出现?
一直做C/S开发的工作,但是很少做和布局相关的工作,往往都是同事将界面设计好,自己填写代码而已,对于B/S的工作,做过,但是很少没有像C/S这么多,界面布局的话,更无从谈起. 日子就这么过,一天一个样 ...
- 循环初练 for
class Program { static void Main(string[] args) { while (true) ...
- Selenium_WebDriver登录模拟鼠标移动切换窗体等操作练习(cssSelector初练手)_Java
cssSelector 据说cssSelector比xpath快. 所以,有固定ID属性的页面元素用By.id或者By.cssSelector("#id属性值")来找,有class ...
- Golang初练手-多线程网站路径爆破
以前用Python写过这个工具,前两天看了golang的基础,就想着用这个语言把这个工具重写一遍 先放张图 用法 Example : Buster.exe -u=https://www.baidu.c ...
- C++ 类的抽象初练
/* 某商店经销一种货物,货物的购进和卖出以箱为单位,各箱的重量不一样, 因此商店需要目前库存的总重量. 现在用c++模拟商店货物购进和卖出的情况 */ #include<iostream> ...
- Windows API初练手 -- 疯狂写文件代码
警告:恶作剧软件,慎用!仅供初学者研究代码所用!!! 提示:默认文件创建目录在"D:\test",如果需要使用的话请自行更改目录. 1. Windows API 版本 (调用系统函 ...
- Python3 实例
一直以来,总想写些什么,但不知从何处落笔. 今儿个仓促,也不知道怎么写,就把手里练习过的例子,整理了一下. 希望对初学者有用,都是非常基础的例子,很适合初练. 好了,Follow me. 一.Pyth ...
随机推荐
- Winform ListView根据条件定位到指定行
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- VS2005 VS2008编译的程序在Win7下以管理员身份运行的设置
在VS2005或者VS2008 里面,直接项目右键---属性---连接器---清单文件---uac执行级别 选择requireAdministrator 重新编译 这样你的程序直接运行就拥有管理 ...
- Berkeley DB分布式探索
明天回家就没有网络,今晚就将整个编写过程记录下来.顺带整理思路以解决未能解决的问题. 标题有点托大,想将Berkeley DB做成分布式存储,感觉很高端的样子,实际上就是通过ssh将Berkeley ...
- CentOS 6.4 下搭建 MongoDB 2.4.9 环境
一.下载MongoDB2.4.9版 下载MongoDB wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.9.tgz 解压Mo ...
- js拖拽3D立方体旋转
这段时间有点闲,所以就自己找些小玩意来练习练习.今天做了一个可以拖拽页面内空白位置3D立方体就会跟着转动的小例子,布局方面用到css3 3D转换技术,通过transform控制旋转实现的. 上个图 代 ...
- Crusher Django Tutorial(5) 使用内置管理员系统
http://crusher-milling.blogspot.com/2013/09/crusher-django-tutorial5-using-admin.html 顺便学习一下FQ Crush ...
- 图解 CSS: 理解样式表的逻辑(转载)
原文:http://www.cnblogs.com/del/archive/2009/02/01/1382141.html 样式表可以是外部的.内联的或嵌入的; 链接外部样式文件一般是:<lin ...
- 【转】微软MVP攻略 (如何成为MVP?一个SQL Server MVP的经验之谈)
一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 初衷 什么是微软MVP? 成为微软MVP的条件? 如何成为微软MVP? (一) 申请时间划分 (二) 前期准备 (三) ...
- Redis集群明细文档
Redis目前版本是没有提供集群功能的,如果要实现多台Redis同时提供服务只能通过客户端自身去实现(Memchached也是客户端实现分布式).目前根据文档已经看到Redis正在开发集群功能,其中一 ...
- mysql 查询
查询数据:select s_name from student limit 1;//限制数量 select * from student where s_id in (select s_id from ...