算法学习中经常被二分问题困扰(太弱了我),于是记录一下
二分查找好用的模板
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int a[N], q, k, n;
int bsearch_front(int x){
int l = -1, r = n;
while( l + 1 < r){
int mid = (l + r) >> 1;
if(a[mid] >= x) r = mid; // if(要找大于等于x的最小的位置) 指向右边则是 r = mid
else l = mid;
}
return r;
}
int bsearch_end(int x){
int l = -1, r = n;
while( l + 1 < r){
int mid = (l + r) >> 1;
if(a[mid] <= x) l = mid;// if(要找小于等于x的最大的位置) 指向左边则是 l = mid
else r = mid;
}
return l;
}
int main()
{
cin >> n >> q;
for (int i = 0; i < n; i ++ )
cin >> a[i];
while(q--){
cin >> k;
if(bsearch_front(k) == n || bsearch_end(k) == -1 || bsearch_front(k) > bsearch_end(k)) cout <<"-1 -1"<< endl;
else cout << bsearch_front(k) << " "<< bsearch_end(k) << endl;
}
}
记忆方法:
当我们寻找大于等于(或大于)x的最小位置时,可行域在右边,指向👉。 故在 if 后对r进行赋值,并在最后返回 r 作为查找答案
当我们寻找小于等于(或小于)x的最大位置时,可行域在左边,指向👈。 故在 if 后对l进行赋值,并在最后返回 l 作为查找答案
二分答案的模板和例题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
typedef long long LL;
int d[N], s[N], t[N], r[N];
LL b[N];
int n,m;
bool check(int x){
for (int i = 1; i <= n; i ++ )
b[i] = r[i] - r[i-1];
for (int i = 1; i <= x; i ++ ){
b[s[i]] -= d[i];
b[t[i] + 1] += d[i];
}
for (int i = 1; i <= n; i ++ ){
b[i] += b[i-1];
if(b[i] < 0) return false;
}
return true;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ){
cin >> r[i];
// 容易犯错: b[i] = r[i] - r[i-1]; 差分数组
}
for (int i = 1; i <= m; i ++ ) cin >> d[i] >> s[i] >> t[i];
int l = 0, r = m + 1;
while(l + 1 < r){
int mid = l + r >> 1;
if(check(mid)) l = mid;
else r = mid;
}
if(l == m) printf("%d", 0);
else printf("%d\n%d", -1, l + 1);
return 0;
}
记忆方法:在二分查找的基础上,把if内判断条件改为check函数
当你设定的可行区在左边👈时(即小于x的情况check函数均返回true),则if后接 l = mid
最后l是满足check的最大解,即最大化可行解,l + 1即不满足
上题中 l == m 当最大值m都满足check时,表示所有订单均成立, l < m则表示 l + 1不满足