Introduction to dichotomy and dichotomy

Posted by shauns2007 on Fri, 14 Jan 2022 23:56:54 +0100

Introduction to dichotomy and dichotomy

Little story

One day DOGGOD_Q borrowed N books from the library. When he got out of the library, the alarm sounded, so the security guard put DOGGOD_Q stopped to check which book is not registered for lending. DOGGOD_Q was about to pass each book under the alarm to find out the book that triggered the alarm, but the security guard showed disdainful eyes: can't you even find two points? So the security guard divided the books into two piles, let the first pile pass the alarm, and the alarm sounded; So he divided the pile of books into two piles... Finally, after checking the log N times, the security guard successfully found the book that caused the alarm, showing a proud and mocking smile. So DOGGOD_Q left with the rest of the books on his back. Since then, the library lost N - 1 books.

1. Introduction to dichotomy

binary search, also known as half interval search and logarithmic search, is an algorithm used to find an element in an ordered array. (so the library lost N-1 Books)

1.1 time complexity

The optimal time complexity of binary search is O(1).

The average time complexity and the worst time complexity of binary search are O(log n). Because in the binary search process, the algorithm halves the query interval every time, for an array with a length of, at most O(log n) times will be searched.

1.2 working principle

Take finding a number in an ascending array as an example.

It inspects the middle element of the current part of the array every time. If the middle element is exactly what it is looking for, it ends the search process; If the middle element is smaller than the searched value, the one on the left will be smaller, and there will be no searched element, so you only need to search on the right; If the intermediate element is greater than the value you are looking for, just look it up on the left.

1.3 space complexity

The space complexity of binary search in recursive version is O(log n).

The space complexity of non recursive binary search is O(1).

1.3.1 recursive version

int binary_search1(int left, int right, int target)
{
    if (left > right)
        return -1;
    int mid = left + (right - left) >> 1;
    if (arr[mid] == target)
        return mid;
    else if (arr[mid] < target)
        return (mid + 1, right, target);
    else
        return (left, mid - 1, target);
}

1.3.2 non recursive version

int binary_search2(int left, int right, int target)
{
    if (left > right)
        return -1;
    while (left <= right)
    {
        int mid = left + ((right - left) >> 1);
        if (arr[mid] == target)
            return mid;
        else if (arr[mid] < target)
            left = mid + 1;
        else
            right = mid - 1;
    }
    return -1;
}

1.3.3 binary search of STL

Lower in ascending sequence_ Bound returns the first iterator whose sequence value is greater than or equal to the parameter target.

Upper in ascending sequence_ Bound returns the first iterator greater than the sequence value of the parameter target.

lower_bound(): returns an iterator pointing to the first element with key value > = key.

upper_bound(): returns an iterator pointing to the first element with key value > key.

int arr[12] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};
cout << (lower_bound(arr, arr + 12, 4) - arr) << endl; //Output 9
cout << (upper_bound(arr, arr + 12, 4) - arr) << endl; //Output 12

More people check the Internet!

2. Common questions

2.1 binary search

It is easier to find a number in an ordered sequence.

2.2 binary search

Typical usage scenario: we are required to find the minimum possible case of the maximum value of a certain condition or the maximum case of the minimum value.

Premise of use:

  1. The answer is in a fixed range

  2. It's not easy to search for an answer, but it's not easy to judge directly

  3. Feasible solutions should be monotonic for intervals, because order can be dichotomous

If the question specifies something with "maximum minimum" or "minimum maximum", then this thing should satisfy the boundedness (obviously) and monotonicity (can be seen) of the binary answer.

2.2.1 [NOIP2015 improvement group] stone jumping

Topic background

The annual "stone jumping" competition will start again!

Title Description

The competition will be held in a straight river with some huge rocks. The organizing committee has selected two rocks as the starting point and ending point of the competition. Between the start point and the end point, there are N Block of rock (excluding starting and ending rocks). During the competition, the contestants will start from the starting point and jump to the adjacent rocks with each step until they reach the end.

In order to improve the difficulty of the competition, the organizing committee plans to remove some rocks to make the shortest jumping distance as long as possible. Due to budget constraints, the organizing committee can move from the starting point to the end point at most M A rock (the rock at the beginning and end cannot be removed).

Input format

The first line contains three integers L,N,M,It represents the distance from the start point to the end point, the number of rocks between the start point and the end point, and the number of rocks removed by the organizing committee.

Output format

An integer, that is, the maximum value of the shortest jump distance.

For example, this is a very typical two-way answer. Search first and enumeration can be done, but it will timeout. The question specifies something with "minimum maximum value" or "maximum minimum value", and meets the premise of two-point answer.

The minimum value and maximum value are between fixed [1, l]. We can dichotomy the value of "minimum value and maximum value", and then judge whether it is OK. If it is OK, go on the right, and if not, go on the left.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int inf = 2147483647;
const double PI = acos(-1.0);
int arr[maxn];
int l, n, m, ans = 0;
bool judge(int x)
{             
    int cnt = 0; //cnt records the actual number of stones to be removed with the current answer
    int cur = 0; //cur stands for the current position of the simulated stone jumper
    int nxt = 0; //nxt stands for the number of the next stone
    while (nxt < n + 1)
    {
        nxt++;
        if (arr[nxt] - arr[cur] < x) //Judge the distance. Just look at the distance between them and calculate the difference
            cnt++; //Determine success, take this stone away and continue to consider the next stone
        else
            cur = nxt; //If the judgment fails, we don't have to take this stone away. We'll jump over and think about the next one
    }
    if (cnt > m)
        return false;
    else
        return true;
}
int main()
{
    scanf("%d %d %d", &l, &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &arr[i]);
    arr[n + 1] = l;
    int left = 1, right = l;
    while (left <= right)
    {
        int mid = left + ((right - left) >> 1);
        if (judge(mid))
        {
            ans = mid;
            left = mid + 1;
        }
        else
            right = mid - 1;
    }
    printf("%d\n", ans);
    return 0;
}

3. Three point introduction

In the previous algorithms, the array or function to be searched must be monotonically increasing or decreasing. When the array or function is not monotonic (such as quadratic function), the bisection algorithm is powerless, because in this case, it is impossible to determine whether the point to be found is on the left or right of the mid. in order to solve this problem, Here is another algorithm, the trisection algorithm.

The trisection algorithm is used to find the maximum value of concave / convex functions (such as quadratic functions) in an interval. The idea of this algorithm is also very similar to the bisection method. As the name suggests, the trisection method is to divide an interval into three parts and narrow the interval step by step through the numerical relationship between the two points in the middle. When the interval is small enough, the boundary of the interval can be regarded as the solution of the function.

The key of the split algorithm is how to determine the position of the maximum value according to the two mids. It is not easy to describe in words. Here, the mids are listed in pictures_ L (middle point on the left) and mid_ Several possible situations of R (the middle point on the right) and the position of the maximum point:

The third row of the table indicates the feasible strategy, and we need to judge the mid_l and mid_r is the relationship between the corresponding function values, and part of the interval is excluded according to this relationship, so as to gradually narrow the interval. Based on the three cases, we only need to exclude an interval far from the maximum point in each cycle (because we can't judge which of the above cases).

double f(double x)
{
    return x * x; //Take y=x^2 as an example
}
double getMinValue(double left, double right)
{
    double mid_l, mid_r;
    //"1E-8" can be understood as the accuracy of x. the smaller the number, the more accurate the result is
    while (right - left > 1E-8)
    {
        //It may or may not be equally divided here
        mid_l = left + (right - left) / 3;
        mid_r = left + (right - left) / 3 * 2;

        if (f(mid_l) > f(mid_r))
            left = mid_l;
        else
            right = mid_r;
    }

    return f(left);
}

Question sheet transmission: Binary search and binary answer - Luogu

Topics: Algorithm