思想转载自:http://blog.csdn.net/pi9nc/article/details/9666627
如果遇到凸性或凹形函数时,可以用三分查找求那个凸点或凹点。
如图所示,已知左右端点L、R,要求找到白点的位置。
思路:通过不断缩小 [L,R] 的范围,无限逼近白点。
做法:先取 [L,R] 的中点 mid,再取 [mid,R] 的中点 mmid,通过比较 f(mid) 与 f(mmid) 的大小来缩小范围。
当最后 L=R-1 时,再比较下这两个点的值,我们就找到了答案。
1、当 f(mid) > f(mmid) 的时候,我们可以断定 mmid 一定在白点的右边。
反证法:假设 mmid 在白点的左边,则 mid 也一定在白点的左边,又由 f(mid) > f(mmid) 可推出 mmid < mid,与已知矛盾,故假设不成立。
所以,此时可以将 R = mmid 来缩小范围。
2、当 f(mid) < f(mmid) 的时候,我们可以断定 mid 一定在白点的左边。
反证法:假设 mid 在白点的右边,则 mmid 也一定在白点的右边,又由 f(mid) < f(mmid) 可推出 mid > mmid,与已知矛盾,故假设不成立。
同理: 当求凹点的时候,当f(m) > f(mm)的时候 ,L =m;
当f(m) < f(mm) 的时候,R = mm;证明与上面一般;
zoj3203:
题意:
人左右走动,求影子L的最长长度。
思路:求最长的影子长度;
设x为人灯光垂直线的距离,那么有两个边界,一个是当影子刚好射到墙角的时候,即x = h / H * D ,一个是全都投射到墙上的情况即x = D;
这个肯定在中间的某个位置最长,利用calc(x)公式求出最长的长度,calc公式推导如下:
可以得出L= H - D * (H - h)/x +D -x,不过得注意x的起点是从h / H * D 开始的;
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 100000 + 10;
#define INF 0x7fffffff
#define clr(x,y) memset(x,y,sizeof(x))
typedef long long ll;
#define eps 10e-10
double H,h,D;
double calc(double x)
{
return H - D * (H - h)/x +D -x;
}
int main()
{
int Tcase;
scanf("%d",&Tcase);
while(Tcase --)
{
scanf("%lf%lf%lf",&H,&h,&D);
double l = D - h /H * D,r = D;
while(l + eps < r)
{
double m = (l + r) / 2, mm = (m + r) /2;
if(calc(m) < calc(mm))
{
l = m;
}
else
{
r = mm;
}
}
printf("%.3lf\n",calc(l));
}
return 0;
}
公式为:L = H + D - ((H - h)*D /x + x),你会发现这就是一个双勾函数,只要把这个函数的最小值,L的最大值就能求出来了,双勾函数的性质,对于一个双勾函数: a*x + b / x .(a> 0 && b > 0)
然后判断一下边界取出最小值代入得到最大值。
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 100000 + 10;
#define INF 0x7fffffff
#define clr(x,y) memset(x,y,sizeof(x))
typedef long long ll;
#define eps 10e-10
double H,h,D;
double calc(double x)
{
return H + D - ((H - h)*D /x + x);
}
int main()
{
int Tcase;
scanf("%d",&Tcase);
while(Tcase --)
{
scanf("%lf%lf%lf",&H,&h,&D);
double xm = sqrt(D*(H - h));
double l = D - h / H * D,r = D;
double ans;
if(l <= xm && r >= xm)
ans = calc(xm);
else if(xm < l)
ans = calc(l);
else ans = calc(r);
printf("%.3lf\n",ans);
}
return 0;
}
HDU 2438
题意:已知汽车的长和宽,l和w,以及俩条路的宽为x和y,汽车所处道路宽为x ,问汽车能否顺利转弯?
分析:汽车能否顺利转弯取决于在极限情况下,随着角度的变化,汽车离对面路的距离是否大于等于0
在上图中需要计算转弯过程中h 的最大值是否小于等于y
很明显,随着角度θ的增大,最大高度h先增长后减小,即为凸性函数,可以用三分法来求解
如图中:
这题转载自http://www.cnblogs.com/nanke/archive/2012/02/12/2348041.html
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1000000 + 10;
#define INF 0x7fffffff
#define clr(x,y) memset(x,y,sizeof(x))
typedef long long ll;
#define eps 10e-10
double x,y,l,w;
double calc_h(double angle)
{
double s = l * cos(angle) + w *sin(angle) - x;
return s * tan(angle) + w * cos(angle);
}
int main()
{
while( ~ scanf("%lf%lf%lf%lf",&x,&y,&l,&w))
{
double L = 0,R = acos(-1.0)/2;
while(L + eps < R)
{
double m = (L + R) / 2,mm = (m + R)/2;
if(calc_h(m) < calc_h(mm))
{
L = m;
}
else R = mm;
}
double max_h = calc_h(L);
if(max_h <= y)
{
cout << "yes" << endl;
}
else cout << "no" << endl;
}
return 0;
}