903 字
5 分钟
竞赛算法(2) 排序, 查找
快速排序
基于分治思想, 对于每个区间将其分为较小的一段和较大的一段, 较小段的最大值小于等于较大段的最小值.
快速排序的步骤
- 选定基准值x, 将某个区间分为小于等于x的部分和大于x的部分, 当x靠近中位数时分治效果较为理想. 通常随机选取区间中的某个数.
- 使用双指针i, j. 其中i从前往后遍历, j从后往前遍历.
- 保证i遍历过的部分都是小于等于x的, j遍历过的都是大于x的.
- 如果i处大于x, j处小于等于x, 并且i在j前面, 交换两处的数字, 并移动i和j
- 如果i不在j前面, 说明已经遍历完毕, break.
- 递归处理左右两段.
void qks(vector<int>& arr, int l, int r) { if (l >= r) return; int x = arr[(l + r) / 2]; int i = l - 1; int j = r + 1; while (i < j) { do i++; while (arr[i] < x); do j--; while (arr[j] > x); if (i < j) swap(arr[i], arr[j]); } qks(arr, l, j); qks(arr, j + 1, r);}归并排序
同样基于分治思想, 见算法导论.
void mgs(vector<int>& arr, int l, int r) { static vector<int> tmp(arr.size()); if (l >= r) return;
int mid = (l + r) / 2; mgs(arr, l, mid), mgs(arr, mid + 1, r);
int i = l, j = mid + 1, k = 0; while (i <= mid && j <= r) { if (arr[i] <= arr[j]) { tmp[k++] = arr[i++]; } else { tmp[k++] = arr[j++]; } } while (i <= mid) { tmp[k++] = arr[i++]; } while (j <= r) { tmp[k++] = arr[j++]; }
for (i = l, k = 0; i <= r; i++, k++) { arr[i] = tmp[k]; }}归并排序用于求逆序对
long long ans = 0;void mgs(vector<int>& arr, int l, int r) { static vector<int> tmp(arr.size()); if (l >= r) return;
int mid = (l + r) / 2; mgs(arr, l, mid), mgs(arr, mid + 1, r);
int i = l, j = mid + 1, k = 0; while (i <= mid && j <= r) { if (arr[i] <= arr[j]) { // 注意这里必须是小于等于 tmp[k++] = arr[i++]; } else { tmp[k++] = arr[j++]; ans += mid - i + 1; // 添加的一行 } } while (i <= mid) { tmp[k++] = arr[i++]; } while (j <= r) { tmp[k++] = arr[j++]; }
for (i = l, k = 0; i <= r; i++, k++) { arr[i] = tmp[k]; }}二分查找
bool check(int a);void bs(const vector<int>& arr) { int n = arr.size(); int l = -1; int r = n; while (r - l > 1) { int mid = (l + r) / 2; if (check(arr[mid])) { l = mid; } else { r = mid; } } return;}// 实数域二分const double PCS = 1e-7;bool check(double a);double bs(double n, double l, double r) { while (r - l > PCS) { double mid = (l + r) / 2; if (check(mid)) { l = mid; } else { r = mid; } } return l;}三分法
求函数的极值. 结论: 如果若干个函数均为单极小值函数, 则他们取max也是单极小值函数. 例题: 1730B 285083A
01分数规划
形式化表述:
给定两个数组A, B. 求一组, 最小化或最大化.
通俗地讲, 这相当于每个物品有两个权值a, b. 取若干个物品, 求a的总和与b的总和的比值.
这类问题通常可以用二分法来解决. 对于每个预测值mid, 我们应该考虑与的比值是否大于等于mid. 这等价于求某个子集使得. 求出左式最大值即可.
例题: 285069A
285069C(nth_element的用法: nth_element(first, nth, last), 其中first, nth, last均为迭代器, 这个函数保证nth位置的数值准确, nth前全部小于nth, nth后全部大于nth. 平均O(n)的复杂度)
竞赛算法(2) 排序, 查找
https://fuwari.vercel.app/posts/竞赛算法2-排序-查找/