類似於二分查找,三分搜索法也是比較常用的基於分治思想的高效查找方法。但是和二分不同,二分只適用於單調函數,比如常用的對單調遞增或單調遞減的一個序列中的某一個元素進行查找,三分卻突破了這種限制,可以用於左邊遞增右邊遞減或者相反的,這麼一類函數,也就是常說的凸函數和凹函數。但是爲什麼三分法可以用於凸函數或者凹函數吶,這其實是因爲這種函數總是有一個最大值或者最小值,這樣就可以藉此判斷出三分法中兩個中點相對相對於極值的位置。
三分搜索的實現主要是判斷midl和midr所在值的大小。以凸函數爲例(凹函數類似,只是判mid大小的時候保留小的即可(其實也是保留離極值最近的mid)),先以left和right爲端點計算出它們的中點midl,然後再以midl和right爲端點計算出它們的中點midr,接下來就需要判斷f(midl)和f(midr)值的大小了,如果f(midl)大於f(midr),那麼說明midl靠近極值,此時令right=midr,否則說明midr靠近極值,此時則令left=midl,總之就是要保留離極值最近的那一個mid,然後重複前面的過程,直到left和right十分接近,最終f(left)就等於了極值。
https://www.cometoj.com/contest/33/problem/B?problem_id=1456
#include <iostream>
#include<stdio.h>
#include<bits/stdc++.h>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
ll n,m,s[200005];
ll solve(ll mid)
{
ll ans=0;
for(ll i=0; i<n; i++)
{
if(s[i]>=mid)
ans+=(s[i]-mid)*(s[i]-mid);
else
ans+=max(mid-s[i]-m,(ll)0)*max(mid-s[i]-m,(ll)0);
}
return ans;
}
int main()
{
cin>>n>>m;
for(ll i=0; i<n; i++)
cin>>s[i];
ll l=1,r=2e5;
while(r-l>1)
{
ll midl=(l+r)/2;
ll midr=(midl+r)/2;
if(solve(midl)>=solve(midr))
l=midl;
else
r=midr;
}
ll ans=solve(l);
ans=min(ans,solve(r));
cout<<ans<<endl;
return 0;
}