OSDN Git Service

style: format markdown files with remark-lint
author24OI-bot <15963390+24OI-bot@users.noreply.github.com>
Thu, 6 Dec 2018 11:52:46 +0000 (19:52 +0800)
committer24OI-bot <15963390+24OI-bot@users.noreply.github.com>
Thu, 6 Dec 2018 11:52:46 +0000 (19:52 +0800)
docs/basic/binary.md

index 3876be1..a310dbc 100644 (file)
@@ -31,9 +31,7 @@ int binary_search(int start, int end, int key) {
 
 ??? note
 
-```
-`>> 1` 比 `/ 2` 速度快一些
-```
+    `>> 1` 比 `/ 2` 速度快一些
 
 注意,这里的有序是广义的有序,如果一个数组中的左侧或者右侧都满足某一种条件,而另一侧都不满足这种条件,也可以看作是一种有序(如果把满足条件看做 $1$,不满足看做 $0$,至少对于这个条件的这一维度是有序的)。换言之,二分搜索法可以用来查找满足某种条件的最大(最小)的值。
 
@@ -41,9 +39,9 @@ int binary_search(int start, int end, int key) {
 
 要想使用二分搜索法来解这种「最大值最小化」的题目,需要满足以下三个条件:
 
-1. 答案在一个固定区间内;
-2. 可能查找一个符合条件的值不是很容易,但是要求能比较容易地判断某个值是否是符合条件的;
-3. 可行解对于区间满足一定的单调性。换言之,如果 $x$ 是符合条件的,那么有 $x + 1$ 或者 $x - 1$ 也符合条件。(这样下来就满足了上面提到的单调性)
+1.  答案在一个固定区间内;
+2.  可能查找一个符合条件的值不是很容易,但是要求能比较容易地判断某个值是否是符合条件的;
+3.  可行解对于区间满足一定的单调性。换言之,如果 $x$ 是符合条件的,那么有 $x + 1$ 或者 $x - 1$ 也符合条件。(这样下来就满足了上面提到的单调性)
 
 当然,最小值最大化是同理的。
 
@@ -61,47 +59,44 @@ 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++
 int a[1000005];
-int n,m;
-bool check(int k)//检查可行性,k为锯片高度
+int n, m;
+bool check(int k)  //检查可行性,k为锯片高度
 {
-    long long sum=0;
-    for (int i=1;i<=n;i++)//检查每一棵树
-        if (a[i]>k)//如果树高于锯片高度
-            sum+=(long long)(a[i]-k);//累加树木长度
-    return sum>=m;//如果满足最少长度代表可行
+  long long sum = 0;
+  for (int i = 1; i <= n; i++)       //检查每一棵树
+    if (a[i] > k)                    //如果树高于锯片高度
+      sum += (long long)(a[i] - k);  //累加树木长度
+  return sum >= m;                   //如果满足最少长度代表可行
 }
-int find(int x)
-{
-    int l=1,r=1000000001;//因为是左闭右开的,所以10亿要加1
-    while (l+1<r)//如果两点不相邻
-    {
-        int mid=(l+r)/2;//取中间值
-        if (check(mid))//如果可行
-            l=mid;//升高锯片高度
-        else
-            r=mid;//否则降低叶片高度
-    }
-    return l;//返回左边值
+int find(int x) {
+  int l = 1, r = 1000000001;  //因为是左闭右开的,所以10亿要加1
+  while (l + 1 < r)           //如果两点不相邻
+  {
+    int mid = (l + r) / 2;  //取中间值
+    if (check(mid))         //如果可行
+      l = mid;              //升高锯片高度
+    else
+      r = mid;  //否则降低叶片高度
+  }
+  return l;  //返回左边值
 }
-int main()
-{
-    cin>>n>>m;
-    for (int i=1;i<=n;i++)
-        cin>>a[i];
-    cout<<find(m);
-    return 0;
+int main() {
+  cin >> n >> m;
+  for (int i = 1; i <= n; i++) cin >> a[i];
+  cout << find(m);
+  return 0;
 }
 ```
 
 看完了上面的代码,你肯定会有两个疑问:
 
-1. 为何搜索区间是左闭右开的?
+1.  为何搜索区间是左闭右开的?
 
 因为搜到最后,会这样(以合法的最大值为例):
 
@@ -113,7 +108,7 @@ int main()
 
 合法的最小值恰恰相反。
 
-1. 为何返回左边值?
+1.  为何返回左边值?
 
 如上图
 
@@ -132,10 +127,10 @@ else
 
 画一下图好理解一些(图待补)
 
-- 如果 `mid` 和 `midmid` 在最大(小)值的同一侧:
-  那么由于单调性,一定是二者中较大(小)的那个离最值近一些,较远的那个点对应的区间不可能包含最值,所以可以舍弃。
-- 如果在两侧:
-  由于最值在二者中间,我们舍弃两侧的一个区间后,也不会影响最值,所以可以舍弃。
+-   如果 `mid` 和 `midmid` 在最大(小)值的同一侧:
+    那么由于单调性,一定是二者中较大(小)的那个离最值近一些,较远的那个点对应的区间不可能包含最值,所以可以舍弃。
+-   如果在两侧:
+    由于最值在二者中间,我们舍弃两侧的一个区间后,也不会影响最值,所以可以舍弃。
 
 ## 分数规划
 
@@ -146,14 +141,19 @@ else
 ### 二分法
 
 比如说我们要求的是最小的,记 $L$ 为最优的答案,对这个式子做一些变换:
+
 $$
 L \geq \frac{\sum{c_i}}{\sum{d_i}}
 $$
+
 把分母乘过去,把右侧化为 $0$:
+
 $$
 {\sum{d_i}} \times L - {\sum{c_i}} \geq 0
 $$
+
 即:
+
 $$
 {\sum_{i=1}^N{d_i}} \times L - {\sum_{i=1}^N{c_i}} \geq 0
 $$