三分查找
概述
- 类似于二分查找,只不过将区间分成了三份。
- 二分查找主要用于单调函数,而三分主要用于单峰函数(包括有限的单调函数)
思路
-
以凹函数为例,以下均以区间左端点认为
l
,右端点为认为r
-
用两个点将区间分成三份
midl = l + (right - left)/3; midr= right - (right - left)/3;
-
与极值点进行比较,社戏远离最值点的一段
- 11如果
midl < midr
令r = midr- 1
- 如果
midr < midl
令l = midl + 1
- 11如果
-
如此不断缩小区间,直到
midl
与minr
近乎相等
代码实现
int l = 1, r = n;
while(l <= r)
{
midl = l + (right - left)/3;
midr= right - (right - left)/3;
if(check(midl) < check(midr))
r = midr - 1;
else l = midl + 1;
}
应用
本题的特点是,当数据中不存在此点时的某些情况需要比较和他相邻的点的结果,及最近的大于它和小于它的。这时候就可以用三分查找,找到此两点。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, m, k;
ll a[N];
ll check(int mid)
{
ll res = sum - a[mid];
ll ans = x - a[mid];
res = y - res;
if(ans < 0) ans = 0;
if(res < 0) res = 0;
return ans + res;
}
int main()
{
ll x, y, sum;
while(scanf("%d", &n) != EOF)
{
for(int i = 1; i <= n; i ++)
{
scanf("%lld", a + i);
sum += a[i];
}
sort(a + 1, a + 1 + n);
scanf("%d", &m);
while(m --)
{
cin >> x >> y;
int l = 1, r = n;
ll ans = 0x7f7f7f7f;
while(r >= l)
{
int midl = l + (r - l) / 3;
int midr = r - (r - l) / 3;
if(check(midl) < check(midr))
{
r = midr - 1;
}
else
{
l = midl + 1;
}
ans = min(ans, min(check(l), check(r)));
}
printf("%lld\n", ans);
}
}
return 0;
}