三分算法總結


和二分非常類似的一個算法,與二分不同的是

二分是單調的,而三分是一個先增后減或者先減后增

三分可以求出峰值。

注意三分一定是嚴格單調的,不能有相等的情況。

不過貌似只有求函數最值才用到這個東西,沒有二分應用范圍那么廣。

「一本通 1.2 例 3」曲線

畫畫圖可以發現,滿足先減后增

圖和雅禮集訓里Merchant那道題非常的像,只不過那道題是最大值,可以用二分。

這道題是最小值,用三分   雅禮集訓

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 1e5 + 10;
double a[MAXN], b[MAXN], c[MAXN];
int n;

double f(double x)
{
	double res = -1e9;
	REP(i, 0, n)
		res = fmax(res, a[i] * x * x + b[i] * x + c[i]);
	return res;
}
 
int main()
{
	int T;
	scanf("%d", &T);
	
	while(T--)
	{
		scanf("%d", &n);
		REP(i, 0, n) scanf("%lf%lf%lf", &a[i], &b[i], &c[i]);
		double l = 0, r = 1e3;
		while(r - l > 1e-11)
		{
			double m1 = l + (r - l) / 3;
			double m2 = r - (r - l) / 3;
			if(f(m1) > f(m2)) l = m1;
			else r = m2;
		}
		printf("%.4lf\n", f(l));
	}
	
    return 0;
}

「一本通 1.2 練習 3」燈泡

這道題我主要是推公式推了好久,我一直算錯……

首先可以分兩種情況

(1)有影子在牆上

(2)沒有影子在牆上

 

沒有影子在牆上的時候,通過計算可以得出當光線照在牆角的時候最大。

設人到牆的距離為x

這個時候我們可以得到x的上界h * D / H(相似)

這個時候就可以合並到第一種情況。

 

第一種情況可以推出影子長度L = x + (D * h - x * H) / (D - x)

不需要化簡,只要在程序中可以算出就行了。

這個時候我就猜測,肯定在某個x是最大的。

我就把x等於各種值得情況打印了出來。

果然,有個最值。

那么就三分 。

可以看到,三分的應用是在有浮點數的題目中求最值。

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

double H, h, D;
inline double f(double x)
{
	return x + (D * h - x * H) / (D - x);
}
 
int main()
{
	int T;
	scanf("%d", &T);
	
	while(T--)
	{
		scanf("%lf%lf%lf", &H, &h, &D);
		double l = 0, r = h / H * D;
		while(r - l > 1e-11)
		{
			double m1 = l + (r - l) / 3;
			double m2 = r - (r - l) / 3;
			if(f(m1) > f(m2)) r = m2;
			else l = m1;
		}
		printf("%.3lf\n", f(l));
	}
	
    return 0;
}

bzoj 1857

這是一道省選題。

首先我先觀察題目可以發現一個性質。

路徑必然是從AB上走一段然后走到CD某個點上然后走到D

首先確定了AB上的一點可以用三分算出最優解

然后我就猜測AB上的點也可以用三分做。

然后就這么做了。

經過長時間的調試。

70分。

然后我就遇到了一些奇怪的問題

sqrt里面要加上EPS,不然相同的點一算為0,浮點數可能弄到負數。10分。

然后我是用參數方程寫的,比較復雜,中間有個地方寫錯了,20分

AC了之后看別人題解發現可以同時維護x坐標的mid和y坐標的mid,且是一一對應的。

我用參數方程就復雜了,思維量和代碼量都上升了。

但我懶得改了。

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const double EPS = 1e-8;
struct node
{
	double x, y;
	void read() { scanf("%lf%lf", &x, &y); }
}a, b, c, d;
double p, q, r;
double cos1, sin1, cos2, sin2;

inline double dis(node a, node b)
{
	return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) + EPS);
}

double f(double t1, double t2)
{
	node k1 = node{a.x + t1 * cos1, a.y + t1 * sin1};
	node k2 = node{c.x + t2 * cos2, c.y + t2 * sin2};
	return dis(a, k1) / p + dis(k1, k2) / r + dis(k2, d) / q;
}

double f1(double t)
{
	double l = 0, r = dis(c, d);
	while(r - l > EPS)
	{
		double m1 = l + (r - l) / 3;
		double m2 = r - (r - l) / 3;
		if(f(t, m1) > f(t, m2)) l = m1;
		else r = m2;
	}
	return f(t, l);
}
 
int main()
{
	a.read(); b.read();
	c.read(); d.read();
	scanf("%lf%lf%lf", &p, &q, &r);
	
	cos1 = (b.x - a.x) / dis(a, b);
	sin1 = (b.y - a.y) / dis(a, b);
	cos2 = (d.x - c.x) / dis(c, d);
	sin2 = (d.y - c.y) / dis(c, d);
	
	double l = 0, r = dis(a, b);
	while(r - l > EPS)
	{
		double m1 = l + (r - l) / 3;
		double m2 = r - (r - l) / 3;
		if(f1(m1) > f1(m2)) l = m1;
		else r = m2;
	}
	printf("%.2lf\n", f1(l));
	
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM