整数二分 单调递增找x或x后继 1 2 3 4 5 6 7 8 9 int bin_search (int *a, int n, intx) { int left = 0 , right = n; while (l < r){ int mid = (l + r) >> 1 ; if (a[mid] >= x) r = mid; else l = mid + 1 ; } return l; }
单调递增找x或x前驱 1 2 3 4 5 6 7 8 9 int bin_search (int *a, int n, intx) { int left = 0 , right = n; while (l < r){ int mid = (l + r) >> 1 ; if (a[mid] <= x) l = mid; else r = mid - 1 ; } return l; }
不建议用 (l + r) / 2 因为”/“不是真正意义上的向下取证 而是 向0取整 当l+r是正数时是向下取整,当l+r是负数时,时向上取整 故推荐用右移的方式
x >> n x右移n 即二进制下所有数向右移动n位 在十进制下就是 $$ x/2^n $$ 左移则反之
例题: P1824 [USACO05FEB] 进击的奶牛 Aggressive Cows G 题目描述 农夫约翰建造了一座有 $n$ 间牛舍的小屋,牛舍排在一条直线上,第 $i$ 间牛舍在 $x_i$ 的位置,但是约翰的 $m$ 头牛对小屋很不满意,因此经常互相攻击。约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其它牛尽可能远的牛舍。也就是要最大化最近的两头牛之间的距离。
牛们并不喜欢这种布局,而且几头牛放在一个隔间里,它们就要发生争斗。为了不让牛互相伤害。约翰决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是多少呢?
输入格式 第一行用空格分隔的两个整数 $n$ 和 $m$;
下面 $n$ 行为 $n$ 个用空格隔开的整数,表示位置 $x_i$。
输出格式 一行一个整数,表示最大的最小距离值。
输入输出样例 #1 输入 #1
输出 #1
说明/提示 【样例解析】把牛放在 $1$,$4$,$8$ 这三个位置,距离是 $3$。容易证明最小距离已经最大。
【数据范围】对于 $100%$ 的数据,$2 \le n \le 10^5$,$0 \le x_i \le 10^9$,$2 \le m \le n$。不保证 $x$ 数组单调递增。
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <bits/stdc++.h> using namespace std;int n, m;int x[100005 ];bool check (int d) { int cnt = 1 ; int last_pos = x[1 ]; for (int i = 2 ; i <= n; i++){ if (x[i] - last_pos >= d){ last_pos = x[i]; cnt++; } } return cnt >= m; } int main () { scanf ("%d %d" ,&n, &m); for (int i = 1 ; i <= n; i++){ scanf ("%d" ,&x[i]); } sort (x+1 ,x+n+1 ); int l = 1 , r = x[n] - x[1 ] + 1 ; int ans; while (l < r){ int mid = (l + r) >> 1 ; if (check (mid)){ ans = mid; l = mid + 1 ; }else { r = mid; } } cout << ans; return 0 ; }
注意 需要让L,R的范围囊括所有可能 一般让R取最大值+1 以防漏掉有边界
实数二分 P1577 切绳子 题目描述 有 $N$ 条绳子,它们的长度分别为 $L_i$。如果从它们中切割出 $K$ 条长度相同的绳子,这 $K$ 条绳子每条最长能有多长?答案保留到小数点后 $2$ 位(直接舍掉 $2$ 位后的小数)。
输入格式 第一行两个整数 $N$ 和 $K$,接下来 $N$ 行,描述了每条绳子的长度 $L_i$ 。
输出格式 切割后每条绳子的最大长度。答案与标准答案误差不超过 $0.01$ 或者相对误差不超过 $1%$ 即可通过。
输入输出样例 #1 输入 #1 1 2 3 4 5 4 11 8.02 7.43 4.57 5.39
输出 #1
说明/提示 对于 $100%$ 的数据 $0<L_i\leq 100000.00,0<n\leq 10000,0<k\leq 10000$
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> using namespace std;int n, k;double ll[10005 ];bool check (double mid) { int cnt = 0 ; for (int i = 1 ; i <= n; i++){ cnt += (int )(ll[i] / mid); } return cnt >= k; } int main () { double maxx = -1 ; scanf ("%d %d" ,&n,&k); for (int i = 1 ; i <= n; i++){ scanf ("%lf" ,&ll[i]); if (ll[i] > maxx) maxx = ll[i]; } double l = 0 , r = maxx; while ((r - l) > 1e-3 ){ double mid = l + (r - l) / 2 ; if (check (mid))l = mid; else r = mid; } printf ("%.2f" ,l); return 0 ; }
while的循环条件是精度