Unverified Commit 751cb2b6 authored by Yaoyao's avatar Yaoyao Committed by GitHub
Browse files

Merge branch 'master' into patch-3

parents 942ed59f 178bc6b3
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -2,8 +2,11 @@

- 请在 commit 的时候写比较有意义的 commit message
- 请给 PR 起比较有意义的标题。       
- 关于同一类尚未合并的 PR 修改请直接在对应的 fork / 分支中进行,除非已有的内容已被合并需要修改,否则**不要多开 PR**
- 请在 PR 之前检查一下您的 PR 是否存在以下常见问题:(确认无问题后请将选项打钩 / 填为 `[x]`
   * [ ] 您的 MD 代码的书写格式,包括但不限于 **中文与英文之间、中文与阿拉伯数字、中文与 LaTeX 公式之间要有一个半角空格**,特别地,在中文全角符号与英文、阿拉伯数字、LaTeX 公式之间,**不需要**半角空格。(这个可以使用自动化工具辅助,比如 https://github.com/baurine/vscode-pangu)  
   * [ ] 请务必确保您的文档中引用的**外链**图片已经全部转存到了**本库内**对应的`images`文件夹中(防止触发某些网站的防盗链),并已全部处理成了`MD文档名称+编号`的形式(可参考已有文档中图片的处理方式)  
   * [ ] 请确保您的文档中的引用链接的稳定性,**不推荐**引用**自建**服务(如OJ)中的资源(如题目)
   * [ ] 对于 LaTeX 公式,请注意常见的问题,**一定要使用** `$\log$``$\min$``$\max$``$\gcd$` 等,而非 `$log$``$min$``$max$``$gcd$`。对于最小公倍数,请使用 `$\operatorname{lcm}$` 而非 `$lcm$`,省略号请使用 `$\cdots$`,叉乘请使用 `$\times$`,点乘请使用 `$\cdot$`
   * [ ] 所有公式中的希腊字母等特殊符号,请不要使用输入法的插入特殊符号功能,而应该使用对应的 LaTeX 公式符号。如 phi 大多数情况下应该使用 `$\varphi$` 而不是 `$\phi$`
   * [ ] 行间公式前后各要有一行空行。
+0 −1
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ script:
  - mkdocs build -v
  - find ./site -type f -name '*.html' -exec node --max_old_space_size=512 ./scripts/render_math.js {} \;
  - npx gulp minify
  - docker build -t oi-wiki:latest .
  - set +e
deploy:
  provider: pages
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
# 欢迎来到 **OI Wiki**!

[![Travis](https://img.shields.io/travis/24OI/OI-wiki.svg?style=flat-square)](https://travis-ci.org/24OI/OI-wiki)
[![Progress](https://img.shields.io/badge/Progress-78%25-brightgreen.svg?style=flat-square)](https://github.com/24OI/OI-wiki)
[![Progress](https://img.shields.io/badge/Progress-84%25-brightgreen.svg?style=flat-square)](https://github.com/24OI/OI-wiki)
[![Uptime Robot Status](https://img.shields.io/uptimerobot/status/m781254113-3e3bac467c64fc99eafd383e.svg?style=flat-square)](https://status.oi-wiki.org/)
[![Telegram](https://img.shields.io/badge/OI--wiki-join%20Telegram%20chat-brightgreen.svg?style=flat-square)](https://t.me/OIwiki)
[![QQ](https://img.shields.io/badge/OI--wiki-join%20QQ%20group-brightgreen.svg?style=flat-square)](https://jq.qq.com/?_wv=1027&k=5EfkM6K)
+20 −22
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@

在二分搜索过程中,每次都把查询的区间减半,因此对于一个长度为 $n$ 的数组,至多会进行 $O(\log n)$ 次查找。

```c++
```cpp
int binary_search(int start, int end, int key) {
  int ret = -1;  // 未搜索到数据返回-1下标
  int mid;
@@ -59,15 +59,14 @@ int binary_search(int start, int end, int key) {

解题的时候往往会考虑枚举答案然后检验枚举的值是否正确。如果我们把这里的枚举换成二分,就变成了“二分答案”。

来看一看一道例题[Luogu  P1873 砍树](https://www.luogu.org/problemnew/show/P1873),我们可以 1 到 1000000000(10 亿)枚举答案,但是这种朴素写法肯定拿不到满分,因为从 1 跑到 10 亿太耗时间。我们可以对答案进行 1 到 10 亿的二分,其中,每次都对其进行检查可行性(一般都是使用贪心法)。 **这就是二分答案。** 
来看一看一道例题 [Luogu P1873 砍树](https://www.luogu.org/problemnew/show/P1873),我们可以 1 到 1000000000(10 亿)枚举答案,但是这种朴素写法肯定拿不到满分,因为从 1 跑到 10 亿太耗时间。我们可以对答案进行 1 到 10 亿的二分,其中,每次都对其进行检查可行性(一般都是使用贪心法)。 **这就是二分答案。** 

下面就是例题的参考答案。

```c++
```cpp
int a[1000005];
int n, m;
bool check(int k)  //检查可行性,k为锯片高度
{
bool check(int k) { //检查可行性,k为锯片高度
  long long sum = 0;
  for (int i = 1; i <= n; i++)       //检查每一棵树
    if (a[i] > k)                    //如果树高于锯片高度
@@ -76,13 +75,12 @@ bool check(int k) //检查可行性,k为锯片高度
}
int find(int x) {
  int l = 1, r = 1000000001;  //因为是左闭右开的,所以10亿要加1
  while (l + 1 < r)           //如果两点不相邻
  {
  while (l + 1 < r) {         //如果两点不相邻
    int mid = (l + r) / 2;    //取中间值
    if (check(mid))           //如果可行
      l = mid;                //升高锯片高度
    else
      r = mid;  //否则降低片高度
      r = mid;  //否则降低片高度
  }
  return l;  //返回左边值
}
@@ -114,29 +112,29 @@ int main() {

## 三分法

```c++
mid = left + (right - left >> 1);
midmid = mid + (right - mid >> 1);  // 对右侧区间取半
if (cal(mid) > cal(midmid))
  right = midmid;
```cpp
lmid = left + (right - left >> 1);
rmid = lmid + (right - lmid >> 1);  // 对右侧区间取半
if (cal(lmid) > cal(rmid))
  right = rmid;
else
  left = mid;
  left = lmid;
```

三分法可以用来查找凸函数的最大(小)值。

画一下图好理解一些(图待补)

-   如果 `mid``midmid` 在最大(小)值的同一侧:
-   如果 `lmid``rmid` 在最大(小)值的同一侧:
    那么由于单调性,一定是二者中较大(小)的那个离最值近一些,较远的那个点对应的区间不可能包含最值,所以可以舍弃。
-   如果在两侧:
    由于最值在二者中间,我们舍弃两侧的一个区间后,也不会影响最值,所以可以舍弃。

## 分数规划

分数规划是这样一类问题,每个物品有两个代价 $c_i$ , $d_i$ ,要求通过某种方式选出若干个,使得 $\frac{\sum{c_i}}{\sum{d_i}}$ 最大或最小。
分数规划是这样一类问题,每个物品有两个属性 $c_i$ , $d_i$ ,要求通过某种方式选出若干个,使得 $\frac{\sum{c_i}}{\sum{d_i}}$ 最大或最小。

经典的例子 最优比率环、最优比率生成树 等等。
经典的例子 最优比率环、最优比率生成树 等等。

### 二分法

@@ -170,4 +168,4 @@ $$

### Dinkelbach 算法

Dinkelbach 算法是每次用上一轮的答案当做新的 $L$ 来输入,不断地迭代,直至答案收敛。
Dinkelbach 算法的大概思想是每次用上一轮的答案当做新的 $L$ 来输入,不断地迭代,直至答案收敛。
+23 −0
Original line number Diff line number Diff line
冒泡排序是一种稳定的排序方法。

以升序为例,冒泡排序每次检查相邻两个元素,如果前面的元素大于后面的元素,就将相邻两个元素交换。当没有相邻的元素需要交换时,排序就完成了。

不难发现,我们最多需要扫描 $n$ 遍数组才能完成排序。

```cpp
void bubble_sort() {
  for (int i = 1; i <= n; i++) {
    bool flag = false;
    for (int j = 1; j < n; j++)
      if (a[j] > a[j + 1]) {
        flag = true;
        int t = a[j];
        a[j] = a[j + 1];
        a[j + 1] = t;
      }
    if (!flag) break;  //如果没有执行交换操作,说明数列已经有序
  }
}
```

在序列完全有序时,该算法只需遍历一遍数组,不用执行任何交换操作,时间复杂度为 $O(n)$ 。在最坏情况下,冒泡排序要执行 $\frac{(n-1)n}{2}$ 次交换操作,时间复杂度为 $O(n^2)$ 。在平均情况下,冒泡排序的时间复杂度也是 $O(n^2)$ 。
 No newline at end of file
Loading