此种写法:
1、保证最终答案处于
[
l
,
r
]
[l,r]
[l,r]内
2、循环以
l
=
r
l=r
l=r结束
3、每次二分的中间值mid会归属于左半段与右半段二者之一
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
当选用 l = mid 的模版时我们需要 l + r + 1;因为否则当 r - l = 1的时候,l + r >> 1的结果为l,如果check返回true就死循环了
注意我们用的是 右移运算>>1 而不是整数除法/2,这是因为右移运算是 向下取整 ,而整数除法是 向零取整,在二分值域包含负数时后者不能正常工作
c++的那两个函数实现了在一个序列中二分查找某个整数的后继
题意 :
#include
using namespace std;
const int N = 1e5 + 10;
int n, q;
int a[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> q;
for (int i = 0; i < n; ++ i) cin >> a[i];
while (q -- ) {
int x;
cin >> x;
int l = 0, r = n - 1;
while (l < r) {
int mid = l + r >> 1;
if (a[mid] >= x) r = mid;
else l = mid + 1;
}
if (a[l] != x) {
cout << "-1 -1\n";
continue;
}
cout << l << ' ';
l = 0, r = n - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (a[mid] <= x) l = mid;
else r = mid - 1;
}
cout << l << endl;
}
}
题意 :
思路 :
// Forward declaration of compare API.
// bool compare(int a, int b);
// return bool means whether a is less than b.
class Solution {
public:
vector<int> specialSort(int N) {
vector<int> ans;
ans.push_back(1);
for (int i = 2; i <= N; ++ i) {
int l = 0, r = ans.size() - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (compare(ans[mid], i)) l = mid;
else r = mid - 1;
}
ans.push_back(i);
for (int j = ans.size() - 2; j >= r + 1; -- j) swap(ans[j], ans[j + 1]); // int j + 1 = ans.size() - 1;
if (!compare(ans[r], i)) swap(ans[r], ans[r + 1]);
}
return ans;
}
};
以 l + eps < r 为循环条件,每次根据在mid上的判定选择r = mid 或 l = mid 分支之一即可
一般需要保留k位小数时,则取
e
p
s
=
1
0
−
(
k
+
2
)
eps=10^{-(k+2)}
eps=10−(k+2)
有时精度不容易确定或表示,就干脆采用循环固定次数的二分方法,这种方法得到的结果的精度通常比设置eps更高
for (int i = 0; i < 100; ++ i) {
double mid = (l + r) / 2;
if (calc(mid)) r = mid;
else l = mid;
)
题意 :
思路 :
对于二分,二分是二分性而不是单调性 只要满足可以找到一个值一半满足一半不满足即可 而不用满足单调性;#include
#define endl '\n'
#define _(a) cout << #a << ": " << (a) << " "
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, m;
int a[N];
double sum[N];
bool check(double avg) {
for (int i = 1; i <= n; ++ i) {
sum[i] = sum[i - 1] + a[i] - avg;
}
double mi = 1e18;
for (int i = m, j = 0; i <= n; ++ i, ++ j) {
mi = min(mi, sum[j]);
if (sum[i] - mi >= 0) return true;
}
return false;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m;
double l = 0, r = 0;
for (int i = 1; i <= n; ++ i) {
cin >> a[i];
r = max(r, (double)a[i]);
}
while (l + 1e-5 < r) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
cout << (int)(r * 1000);
}
单峰函数拥有 唯一的极大值点,在极大值点左侧严格单调上升,右侧严格单调下降;或者拥有 唯一的极小值点,…;我们可以通过三分法求其极值
以单峰函数为例:
1、若 f(lmid) < f(rmid):两种情况下,极大值点都在 lmid右侧,可令 l = lmid
2、若 f(lmid) > f(rmid):极大值点在 rmid左侧,可令 r = rmid
3、若碰到 f(lmid) = f(rmid),当函数严格单调时,令l = lmid 或 r = rmid 均可
题意 :
#include
#define endl '\n'
#define _(a) cout << #a << ": " << (a) << " "
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 15;
int n;
double l, r;
double a[N];
// 秦九韶算法,已知多项式系数,由高到低遍历系数,求得多项式的值
inline double f(double x) {
double ans = 0;
for (int i = n; i >= 0; -- i) {
ans = ans * x + a[i];
}
return ans;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> l >> r;
for (int i = n; i >= 0; -- i) cin >> a[i]; // 系数从高到低输入
// 单峰函数
while (r - l > 1e-7) {
double midl = (l + l + r) / 3;
double midr = (l + r + r) / 3;
if (f(midl) < f(midr)) l = midl; // l = midl
else r = midr; // r = midr
}
printf("%.5lf", l);
}
题意 :
思路 :
#include
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, m;
int a[N];
bool check(int mid) {
int cnt = 1, now = mid;
for (int i = 1; i <= n; ++ i) {
if (a[i] > mid) return false;
if (now >= a[i]) now -= a[i];
else cnt ++ , now = mid - a[i];
}
return (cnt <= m);
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m;
ll sum = 0;
int mx = 0;
for (int i = 1; i <= n; ++ i) cin >> a[i], sum += a[i], mx = max(mx, a[i]);
int l = mx, r = (int)min(1000000000ll, sum);
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l;
}
if (a[i] > mid) return false;避开#include
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, m;
int a[N];
bool check(int mid) {
int cnt = 1, now = mid;
for (int i = 1; i <= n; ++ i) {
if (a[i] > mid) return false;
if (now >= a[i]) now -= a[i];
else cnt ++ , now = mid - a[i];
}
return (cnt <= m);
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m;
ll sum = 0;
for (int i = 1; i <= n; ++ i) cin >> a[i], sum += a[i];
int l = 0, r = (int)min(1000000000ll, sum);
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l;
}
#include
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, m;
int a[N];
bool check(int mid) {
int cnt = 1, now = mid;
for (int i = 1; i <= n; ++ i) {
if (now >= a[i]) now -= a[i];
else cnt ++ , now = mid - a[i];
}
return (cnt <= m);
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m;
ll sum = 0;
int mx = 0;
for (int i = 1; i <= n; ++ i) cin >> a[i], sum += a[i], mx = max(mx, a[i]);
int l = mx, r = (int)min(1000000000ll, sum);
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l;
}