// 二分查找 int left, right, mid, ans; left = 1; right = n; ans = -1; while (left <= right) { mid = left + (right-left) / 2; if (v[mid] > a) { right = mid - 1; } elseif (v[mid] < a) { left = mid + 1; } else { ans = mid; break; } } cout << ans << " ";
int v[1000010]; int n, m, a; intmain(){ scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", v+i); while (m--) { scanf("%d", &a); int left, right, mid, ans; left = 1; right = n; ans = -1; while (left <= right) { mid = left + (right-left)/2; if (v[mid] > a) { right = mid - 1; } elseif (v[mid] < a) { left = mid + 1; } else { // 如果找到,则比较 ans 的值,更新它 if (ans == -1 || ans > mid) ans = mid; right = mid - 1; } } cout << ans << " "; } cout << endl; return0; }
int v[1000010]; int n, m, a; intmain(){ scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", v+i); while (m--) { scanf("%d", &a); int l, r, mid; l = 1; r = n+1; while (l < r) { mid = l + (r-l)/2; if (a > v[mid]) l = mid + 1; else r = mid; } if (l < n+1 && v[l] == a) cout << l << " "; else cout << -1 << " "; } cout << endl; return0; }
如果记不清楚,就分开写:
如果猜对了但要找最小值,就更新 r
如果 mid 大了,则答案在 mid 左侧,就更新 r
如果 mid 小了,则答案在 mid 右侧,就更新 l
另外,以上这种代码其实是不停在[l,mid) 和 [mid+1, r)之间做选择,所以:
l 只会更新成 mid+1
r 只会更新成 mid
最后答案如果有,则在 l 位置,当然 l 位置也可能不是答案:
如果目标极小,没找到,则 l 位置为查找的范围最左侧下标
如果目标极大,没找到,则 l 位置为最初的 r 的位置(那个位置是最后一个元素的下一个位置,直接读取会数组越界)
lower_bound
其实上面那个写法就是 C++ STL 里面的 lower_bound 函数,所以我们可以直接用 lower_bound 函数来实现 P2249 题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#include<bits/stdc++.h> usingnamespace std;
int v[1000010]; int n, m, a; intmain(){ scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", v+i); while (m--) { scanf("%d", &a); int l = lower_bound(v+1, v+n+1, a) - v; if (l < n+1 && v[l] == a) cout << l << " "; else cout << -1 << " "; } cout << endl; return0; }
函数 lower_bound 在 [first,last) 中的前闭后开区间进行二分查找,返回大于或等于目标值的第一个元素位置。如果所有元素都小于目标值,则返回 last 的位置。
// 如果目标值等于或者小于 mid,则 r = m // 如果目标值大于 mid,则 l = m+1 intlower_bound(int a){ int l, r; l = 0; r = n; while (l < r) { int m = l + (r-l)/2; if (a > v[m]) l = m+1; else r = m; } return l; }
// 如果 mid 值小于等于目标,就 l=m+1 // 如果 mid 值大于目标,就 r=m intupper_bound(int a){ int l, r; l = 0; r = n; while (l < r) { int m = l + (r-l)/2; if (a >= v[m]) l = m+1; else r = m; } return l; }
intmain(){ scanf("%d%d", &m, &n); for (int i = 0; i < m; ++i) scanf("%d", vm+i); sort(vm, vm+m); for (int i = 0; i < n; ++i) { scanf("%d", &a); int diff = INT_MAX; int idx = upper_bound(vm, vm+m, a)-vm; if (idx != m) diff = min(diff, abs(vm[idx]-a)); if (idx - 1 >=0 ) diff = min(diff, abs(vm[idx-1]-a)); ans += diff; } cout << ans << endl; return0; }
int n, k; int v[100010]; boolcheck(int mid){ int cnt = 0; for (int i = 0; i < n; ++i) { cnt += v[i]/mid; if (cnt >= k) returntrue; } returnfalse; } intmain(){ scanf("%d%d", &n, &k); for (int i = 0; i < n; ++i) { scanf("%d", v+i); } int left = 1; int right = (int)1e8; int ans = 0; while (left <= right) { int mid = (left + right) / 2; if (check(mid)) { left = mid + 1; ans = max(ans, mid); } else { right = mid - 1; } } cout << ans << endl; return0; }
P2678 跳石头
二分答案:用 mid 去试跳,如果间距小于 mid,则去掉那个石头,如果去掉个数超过 k 个,则失败。
int n, m, v[100010]; boolcheck(int mid){ int tot = 0; int cnt = 0; for (int i = 0; i < n; ++i) { cnt += v[i]; if (v[i] > mid) returnfalse; if (cnt > mid) { tot++; cnt = 0; i--; } } if (cnt != 0) tot++; if (tot <= m) returntrue; elsereturnfalse; }
intmain(){ scanf("%d%d", &n, &m); for (int i = 0; i < n; ++i) { scanf("%d", v+i); } int left = 1; int right = (int)(1e9 + 1); int ans = INT_MAX; while (left <= right) { int mid = (left+right)/2; if (check(mid)) { ans = min(ans, mid); right = mid - 1; } else { left = mid + 1; } } cout << ans << endl; return0; }