二分法
1. 二分查找法代码实现:
1 // 二分查找法 2 // A[]为严格递增序列,left为二分下界,x为欲查询的数 3 // 二分区间为左闭右闭的[left, right], 传入的初值为[0, n-1] 4 int binarySearch(int A[], int left, int right, int x){ 5 int mid; 6 while (left <= right){ 7 mid = (left + right) / 2; // mid = left + (right - left) / 2; 8 if (A[mid] == x){ 9 return mid; 10 } 11 else if (A[mid] > x){ 12 right = mid - 1; 13 } 14 else if (A[mid] < x){ 15 left = mid + 1; 16 } 17 } 18 19 return -1; 20 }
2. 二分法求序列中第一个大于等于x的元素的位置L
1 // 求序列中第一个大于等于x的元素的位置L 2 // 二分上下界为左闭右闭的[left, right], 传入的初值为[0, n] 3 int lower_bound(int A[], int left, int right, int x){ 4 int mid; 5 while (left < right){ 6 mid = (left + right) / 2; 7 if (A[mid] >= x){ 8 right = mid; 9 } 10 else{ 11 left = mid + 1; 12 } 13 } 14 return left; // 返回夹出来的位置 15 }
3. 二分法求序列中第一个大于x 的元素的位置R
1 int upper_bound(int A[], int left, int right, int x){ 2 int mid; 3 while (left < right){ 4 mid = (left + right) / 2; 5 if (A[mid] > x){ 6 right = mid; 7 } 8 else{ 9 left = mid + 1; 10 } 11 } 12 return left; 13 }
4. 二分法解决“寻找有序序列第一个满足某条件的元素的位置”问题的固定模板
1 // 二分区间为左闭右闭[left, right], 初值必须能覆盖解的所有取值 2 int solve(int left, int right){ 3 int mid; 4 while (left < right){ 5 mid = (left + right) / 2; 6 if (条件成立){ 7 right = mid; 8 } 9 else{ 10 left = mid + 1; 11 } 12 } 13 14 return left; 15 }
5. 二分法求根号2的近似值
1 double calSqrt(){ 2 double left = 1, right = 2, mid; // [left, right] = [1, 2] 3 while (right - left > eps){ 4 mid = (left + right) /2; 5 if (f(mid) > 2){ 6 right = mid; 7 } 8 else{ 9 left = mid; 10 } 11 } 12 13 return mid; 14 }
6. 二分法求某个单调函数根的近似值
1 // 利用二分法求单调函数的根 2 const double eps2 = 1e-5; 3 double f(double x){ 4 return ...; // 写函数的表达式 5 } 6 7 double solve(double L, double R){ 8 double left = L, right = R, mid; 9 while (right - left > eps2){ 10 mid = (left + right) / 2; 11 if (f(mid) > 0){ 12 right = mid; 13 } 14 else{ 15 left = mid; 16 } 17 } 18 return mid; 19 }
7. 快速幂的递归写法(二分法实现)
1 // 快速幂的递归写法 2 typedef long long LL; 3 // a^b & m 4 LL binaryPow(LL a, LL b, LL m){ 5 if (b == 0) 6 return 1; 7 if (b & 1){ // 如果b为奇数 8 return a * binaryPow(a, b - 1, m) % m; 9 } 10 else{ 11 LL mu = binaryPow(a, b / 2, m); 12 return mu * mu % m; 13 } 14 }
二分使用实战:
给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列。
现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。
输入格式:
输入第一行给出两个正整数 N 和 p,其中 N(≤105)是输入的正整数的个数,p(≤109)是给定的参数。第二行给出 N 个正整数,每个数不超过 109。
输出格式:
在一行中输出最多可以选择多少个数可以用它们组成一个完美数列。
输入样例:
10 8
2 3 20 4 5 1 6 7 8 9
输出样例:
8
分析:输入数据到数组,对数组进行排序,对每个元素用二分法求出第一个大于 a[i] * p 的元素的位置 j ,(j - i) 即为完美数列的长度,将所有长度取最大值即为结果
代码:这里用到了上面求序列中第一个大于x 的元素的位置R的函数upper_bound()
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 #include <algorithm> 5 using namespace std; 6 7 long long a[100010] = { 0 }; 8 9 long long upper_bound(long long A[], long long left, long long right, long long x){ 10 long long mid; 11 while (left < right){ 12 mid = (left + right) / 2; 13 if (A[mid] > x){ 14 right = mid; 15 } 16 else{ 17 left = mid + 1; 18 } 19 } 20 return left; 21 } 22 23 int main() 24 { 25 freopen("in.txt", "r", stdin); 26 long long ans = 1; // 存储“完美数列”的最大长度 27 long long n, p; 28 scanf("%lld %lld", &n, &p); 29 30 // 读取输入 31 for (long i = 0; i < n; i++){ 32 scanf("%lld", &a[i]); 33 } 34 // 排序 35 sort(a, a + n); 36 // 遍历数组,对每个元素都查找最大值的位置j,区间长度与ans比较,取较大者 37 for (long long i = 0; i < n; i++){ 38 // 寻找第一个大于a[i] * p的元素的位置 39 long long j = upper_bound(a, i + 1, n, a[i] * p); 40 ans = max(ans, j - i); 41 } 42 printf("%lld\n", ans); 43 fclose(stdin); 44 return 0; 45 }