Как найти нули в массиве

Improve Article

Save Article

Like Article

  • Read
  • Discuss(50+)
  • Improve Article

    Save Article

    Like Article

    Given an array of 1s and 0s which has all 1s first followed by all 0s. Find the number of 0s. Count the number of zeroes in the given array.
    Examples : 

    Input: arr[] = {1, 1, 1, 1, 0, 0}
    Output: 2
    
    Input: arr[] = {1, 0, 0, 0, 0}
    Output: 4
    
    Input: arr[] = {0, 0, 0}
    Output: 3
    
    Input: arr[] = {1, 1, 1, 1}
    Output: 0

    Approach 1: A simple solution is to traverse the input array. As soon as we find a 0, we return n – index of first 0. Here n is number of elements in input array. Time complexity of this solution would be O(n).

    Implementation of above approach is below:

    C++

    #include <bits/stdc++.h>

    using namespace std;

    int firstzeroindex(int arr[], int n)

    {

        for (int i = 0; i < n; i++) {

            if (arr[i] == 0) {

                return i;

            }

        }

        return -1;

    }

    int main()

    {

        int arr[] = { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 };

        int n = sizeof(arr) / sizeof(arr[0]);

        int x = firstzeroindex(arr, n);

        if (x == -1) {

            cout << "Count of zero is 0" << endl;

        }

        else {

            cout << "count of zero is " << n - x << endl;

        }

        return 0;

    }

    Java

    import java.io.*;

    class GFG {

        static int firstzeroindex(int arr[], int n)

        {

            for (int i = 0; i < n; i++) {

                if (arr[i] == 0) {

                    return i;

                }

            }

            return -1;

        }

        public static void main(String[] args)

        {

            int arr[] = { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 };

            int n = arr.length;

            int x = firstzeroindex(arr, n);

            if (x == -1) {

                System.out.println("Count of zero is 0");

            }

            else {

                System.out.print("count of zero is ");

                  System.out.println(n-x);

            }

        }

    }

    Python3

    class GFG :

        @staticmethod

        def  firstzeroindex( arr,  n) :

            i = 0

            while (i < n) :

                if (arr[i] == 0) :

                    return i

                i += 1

            return -1

        @staticmethod

        def main( args) :

            arr = [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]

            n = len(arr)

            x = GFG.firstzeroindex(arr, n)

            if (x == -1) :

                print("Count of zero is 0")

            else :

                print("count of zero is ", end ="")

                print(n - x)

    if __name__=="__main__":

        GFG.main([])

    C#

    using System;

    public class GFG{

              static int firstzeroindex(int[] arr, int n)

        {

            for (int i = 0; i < n; i++) {

                if (arr[i] == 0) {

                    return i;

                }

            }

            return -1;

        }

        public static void Main()

        {

            int[] arr = { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 };

            int n = arr.Length;

            int x = firstzeroindex(arr, n);

            if (x == -1) {

                  Console.WriteLine("Count of zero is 0");

            }

            else {

                Console.Write("count of zero is ");

                  Console.WriteLine(n-x);

            }

        }

    }

    Javascript

    <script>

    function firstzeroindex(arr, n)

        {

            for (let i = 0; i < n; i++) {

                if (arr[i] == 0) {

                    return i;

                }

            }

            return -1;

        }

            let arr = [ 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ];

            let n = arr.length;

            let x = firstzeroindex(arr, n);

            if (x == -1) {

                document.write("Count of zero is 0");

            }

            else {

                document.write("count of zero is " + (n-x));

            }

    </script>

    Time complexity: O(n) where n is size of arr.

    Space Complexity: O(1) as we are not using any extra space.

    Approach 2: Since the input array is sorted, we can use Binary Search to find the first occurrence of 0. Once we have index of first element, we can return count as n – index of first zero.

    Implementation:

    C

    #include <stdio.h>

    int firstZero(int arr[], int low, int high)

    {

        if (high >= low)

        {

            int mid = low + (high - low)/2;

            if (( mid == 0 || arr[mid-1] == 1) && arr[mid] == 0)

                return mid;

            if (arr[mid] == 1)

                return firstZero(arr, (mid + 1), high);

            else

                return firstZero(arr, low, (mid -1));

        }

        return -1;

    }

    int countZeroes(int arr[], int n)

    {

        int first = firstZero(arr, 0, n-1);

        if (first == -1)

            return 0;

        return (n - first);

    }

    int main()

    {

        int arr[] = {1, 1, 1, 0, 0, 0, 0, 0};

        int n = sizeof(arr)/sizeof(arr[0]);

        printf("Count of zeroes is %d", countZeroes(arr, n));

        return 0;

    }

    C++

    #include <bits/stdc++.h>

    using namespace std;

    int firstZero(int arr[], int low, int high)

    {

        if (high >= low)

        {

            int mid = low + (high - low) / 2;

            if ((mid == 0 || arr[mid - 1] == 1) &&

                             arr[mid] == 0)

                return mid;

            if (arr[mid] == 1)

                return firstZero(arr, (mid + 1), high);

            else

                return firstZero(arr, low, (mid -1));

        }

        return -1;

    }

    int countZeroes(int arr[], int n)

    {

        int first = firstZero(arr, 0, n - 1);

        if (first == -1)

            return 0;

        return (n - first);

    }

    int main()

    {

        int arr[] = {1, 1, 1, 0, 0, 0, 0, 0};

        int n = sizeof(arr) / sizeof(arr[0]);

        cout << "Count of zeroes is "

             << countZeroes(arr, n);

        return 0;

    }

    Java

    class CountZeros

    {

        int firstZero(int arr[], int low, int high)

        {

            if (high >= low)

            {

                int mid = low + (high - low) / 2;

                if ((mid == 0 || arr[mid - 1] == 1) && arr[mid] == 0)

                    return mid;

                if (arr[mid] == 1)

                    return firstZero(arr, (mid + 1), high);

                else

                    return firstZero(arr, low, (mid - 1));

            }

            return -1;

        }

        int countZeroes(int arr[], int n)

        {

            int first = firstZero(arr, 0, n - 1);

            if (first == -1)

                return 0;

            return (n - first);

        }

        public static void main(String[] args)

        {

            CountZeros count = new CountZeros();

            int arr[] = {1, 1, 1, 0, 0, 0, 0, 0};

            int n = arr.length;

            System.out.println("Count of zeroes is " + count.countZeroes(arr, n));

        }

    }

    Python3

    def firstZero(arr, low, high):

        if (high >= low):

            mid = low + int((high - low) / 2)

            if (( mid == 0 or arr[mid-1] == 1)

                          and arr[mid] == 0):

                return mid

            if (arr[mid] == 1):

                return firstZero(arr, (mid + 1), high)

            else:

                return firstZero(arr, low, (mid - 1))

        return -1

    def countZeroes(arr, n):

        first = firstZero(arr, 0, n - 1)

        if (first == -1):

            return 0

        return (n - first)

    arr = [1, 1, 1, 0, 0, 0, 0, 0]

    n = len(arr)

    print("Count of zeroes is",

            countZeroes(arr, n))

    C#

    using System;

    class CountZeros

    {

        int firstZero(int []arr, int low, int high)

        {

            if (high >= low)

            {

                int mid = low + (high - low) / 2;

                if ((mid == 0 || arr[mid - 1] == 1) &&

                                     arr[mid] == 0)

                    return mid;

                if (arr[mid] == 1)

                    return firstZero(arr, (mid + 1), high);

                else

                    return firstZero(arr, low, (mid - 1));

            }

            return -1;

        }

        int countZeroes(int []arr, int n)

        {

            int first = firstZero(arr, 0, n - 1);

            if (first == -1)

                return 0;

            return (n - first);

        }

        public static void Main()

        {

            CountZeros count = new CountZeros();

            int []arr = {1, 1, 1, 0, 0, 0, 0, 0};

            int n = arr.Length;

            Console.Write("Count of zeroes is " +

                           count.countZeroes(arr, n));

        }

    }

    PHP

    <?php

    function firstZero($arr, $low, $high)

    {

        if ($high >= $low)

        {

            $mid = $low + floor(($high - $low)/2);

            if (( $mid == 0 || $arr[$mid-1] == 1) &&

                                     $arr[$mid] == 0)

                return $mid;

            if ($arr[$mid] == 1)

                return firstZero($arr, ($mid + 1), $high);

            else

                return firstZero($arr, $low,

                                ($mid - 1));

        }

        return -1;

    }

    function countZeroes($arr, $n)

    {

        $first = firstZero($arr, 0, $n - 1);

        if ($first == -1)

            return 0;

        return ($n - $first);

    }

        $arr = array(1, 1, 1, 0, 0, 0, 0, 0);

        $n = sizeof($arr);

        echo("Count of zeroes is ");

        echo(countZeroes($arr, $n));

    ?>

    Javascript

    <script>

        function firstZero(arr , low , high) {

            if (high >= low) {

                var mid = low + parseInt((high - low) / 2);

                if ((mid == 0 || arr[mid - 1] == 1) && arr[mid] == 0)

                    return mid;

                if (arr[mid] == 1)

                    return firstZero(arr, (mid + 1), high);

                else

                    return firstZero(arr, low, (mid - 1));

            }

            return -1;

        }

        function countZeroes(arr , n)

        {

            var first = firstZero(arr, 0, n - 1);

            if (first == -1)

                return 0;

            return (n - first);

        }

            var arr = [ 1, 1, 1, 0, 0, 0, 0, 0 ];

            var n = arr.length;

            document.write("Count of zeroes is " + countZeroes(arr, n));

    </script>

    Output

    Count of zeroes is 5

    Time Complexity: O(Logn) where n is number of elements in arr[].

    Auxiliary Space: O(logn)

    Last Updated :
    20 Feb, 2023

    Like Article

    Save Article

    yyking

    0 / 0 / 0

    Регистрация: 13.10.2018

    Сообщений: 25

    1

    Найти первый и последний нулевые элементы массива, вывести их индексы

    29.11.2018, 00:04. Показов 3919. Ответов 4

    Метки массивы c++ (Все метки)


    Студворк — интернет-сервис помощи студентам

    C++
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    #include<iostream>
    #include <cstdlib>
    #include <ctime>
    int main()
    {
        using namespace std;
        setlocale(0, "");
        const int sizeOfArray = 30;
        int a[sizeOfArray];
        int i = 0;
        srand((unsigned)time(NULL));
        for (i; i < sizeOfArray; i++)
        {
            a[i] = rand() % 100;
            cout << a[i] << " ";
        }
        for (i; i < sizeOfArray; i++)
        {
            if (a[i] == 0)
                cout << "Нулевые элементы в массиве: " << i << endl;
        }
        system("pause");
        return 0;
    }

    В массиве несколько нулевых элементов. Найти первый и последний нулевые элементы. Вывести их индексы.

    Не выводит номера нулевых элементов , не знаю в чем дело .Хелп плиз



    0



    Параллельный Кот

    1905 / 827 / 350

    Регистрация: 25.03.2016

    Сообщений: 2,045

    29.11.2018, 00:10

    2

    Какое значение имеет переменная i в начале выполнения каждого из циклов?

    Добавлено через 1 минуту
    Ваша программа будет выводить индексы всех нулевых элементов, а по условию требуется только для первого и последнего.



    0



    1174 / 835 / 359

    Регистрация: 26.02.2015

    Сообщений: 3,743

    29.11.2018, 00:22

    3

    del



    0



    Yetty

    7427 / 5021 / 2891

    Регистрация: 18.12.2017

    Сообщений: 15,694

    29.11.2018, 00:49

    4

    Лучший ответ Сообщение было отмечено yyking как решение

    Решение

    C++
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    #include <iostream>
    using namespace std;
     
    int main()
    {
        int n, k=0, ifirst=0, ilast=0;
        cout <<"n="; cin >>n;
     
        double*a = new double[n];
     
        cout <<"Enter "<<n<<" elements:n";
        for (int i = 0; i < n; i++)
          {
          cin >>a[i];
          if (a[i]==0)
          {
              k++;
              if (k==1) ifirst=i;
              ilast=i;            
          }
          }
          
          cout <<"firts_index="<<ifirst<<"nlast_index="<<ilast<<"n";
     
        delete[]a;
    system("pause");
    return 0;
    }



    1



    Nishen

    1174 / 835 / 359

    Регистрация: 26.02.2015

    Сообщений: 3,743

    29.11.2018, 00:52

    5

    Ужас.

    C++
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    
    #include <algorithm>
    #include <array>
    #include <iostream>
    #include <iterator>
    #include <random>
     
     
     
    int main() {
     
        constexpr std::size_t size = 30;
        std::array<int, size> array;
     
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dist(-50, 50);
     
        std::generate(array.begin(), array.end(), [&gen, dist]() { return dist(gen); });
        std::copy(array.begin(), array.end(), std::ostream_iterator<int>(std::cout, " "));
     
        auto itFirst = std::find(array.begin(), array.end(), 0);
        auto itLast = std::find(array.rbegin(), array.rend(), 0).base() - 1;
     
        if (itFirst != array.end()) {
     
            if (itFirst != itLast && itLast != array.end()) {
     
                std::cout << "nThere are several elements equals to zero in the array. The first "
                    << "element is in position " << std::distance(array.begin(), itFirst)
                    << ", the last element is in " << std::distance(array.begin(), itLast) << 'n';
     
            } else {
     
                std::cout << "nThere are one element equals to zero in the array. It is in position "
                    << std::distance(array.begin(), itFirst) << 'n';
     
            }
     
        } else {
     
            std::cout << "nThere are no elements equals to zero in the arrayn";
     
        }
     
        system("PAUSE");
        return 0;
     
    }



    0



    var
      i, j, n, m: Integer;
      RowWithMaxZeroes: Integer;
      MaxZeroesInRow: Integer;
      ZeroesInRow: Integer;
      A: array [1..100, 1..100] of Integer;
    begin
        ReadLn(n);
        {Здесь нужно проверить, что n не больше 100 и что-то сделать, если оно больше}
        ReadLn(m);
        {Здесь нужно проверить, что m не больше 100 и что-то сделать, если оно больше}
        RowWithMaxZeroes := 0;
        MaxZeroesInRow := 0;
        for i := 1 to n do
        begin
          ZeroesInRow := 0;
          for j := 1 to m do
          begin
            Read(A[i, j]);
            if A[i, j] = 0  then
              ZeroesInRow := ZeroesInRow + 1;
          end;
          if ZeroesInRow > MaxZeroesInRow then
          begin
            MaxZeroesInRow := ZeroesInRow;
            RowWithMaxZeroes := i;
          end;
        end;
        WriteLn('Row with max zeroes: ', RowWithMaxZeroes);
    end.
    

    После ввода m и n хорошо бы проверить, что они не больше 100 🙂

    P.S.: l плохое имя для переменной, оно путается с i.

    Сайт переезжает. Большинство статей уже перенесено на новую версию.
    Скоро добавим автоматические переходы, но пока обновленную версию этой статьи можно найти там.

    Бинарный поиск

    • Линейный поиск
    • Бинарный поиск
    • Бинарный поиск с вещественными числами
    • Поиск максимума выпуклой функции: тернарный поиск, бинарный поиск
    • Бинарный поиск по ответу

    Линейный поиск

    Мы считаем, что вы уже знаете линейный поиск, а именно умеете решать задачи такого типа:

    • Проверить, есть ли в массиве число X
    • Найти максимум в массиве
    • Найти сумму чисел в массиве
    • Найти первое четное число в массиве

    Все такие задачи решаются с помощью одного прохода по массиву с помощью цикла for. Все такие алгоритмы работают за (O(n)). И даже можно понять, что быстрее, чем (O(n)) решить ни одну из этих задач не получится.

    Задание

    Убедитесь, что вы умеете решать эти задачи. Докажите, что быстрее, чем O(n) их решить в худшем случае нельзя.

    Бинарный поиск

    Однако иногда найти число X в массиве можно и быстрее! Для этого надо добавить условие на то, что массив отсортирован. Но давайте начнем не с этого.

    Задание

    Я загадал число X от 1 до 100. Вы можете спрашивать, больше ли мое число чем число T, я отвечаю “да” или “нет”. За сколько вопросов в худшем случае вы сможете найти число X? Как нужно действовать?

    Решение и состоит в идее бинарного (двоичного) поиска – нужно первым вопросом спросить “число X больше, чем 50?”. После этого, если ответ “нет”, надо спросить “число X больше, чем 25”? И так далее, нужно уменьшать отрезок возможных значений в два раза каждый раз.

    Почему нужно делить обязательно пополам? Почему бы не спросить “число X больше, чем 80?” первым же вопросом? Но если вдруг ответ “нет”, то мы останемся с 80 вариантами вместо 100. То есть деление отрезка ровно пополам гарантирует, что в худшем случае мы останемся не более чем с половиной вариантов.

    Чтобы понять, как быстро это работает, введём новую математическую функцию. Логарифмом по основанию (a) от (b) будем называть число (c), такое что (a ^ c = b). Обозначается как (log_a b = c). Чаще всего мы будем работать с двоичным логарифмом, то есть в какую степень (c) нужно возвести двойку, чтобы получить (b). Поэтому договоримся, что запись (log n) означает двочный логарифм (n).

    Теперь вернёмся к нашей задаче. Можно понять, что такой алгоритм работает как раз за (O(log n)) вопросов (если число 100 на заменить абстрактную переменную (n)). Несложно убедиться, что именно логарифм раз нужно поделить число на два, чтобы получилось 1.

    Общий принцип

    А теперь представьте такую задачу: у вас есть массив, состоящий из некоторого количества подряд идущих нулей, за которыми следует какое-то количество подряд идущих единиц.

    a = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
    n = len(a)
    n
    14

    Вам дан массив, и вам нужно найти позицию первой единицы, то есть найти такое место, где заканчиваются нули, и начинаются единицы. Это можно сделать с помощью линейного поиска за один проход по массиву, но хочется сделать это быстрее.

    Давайте обратимся к идее бинарного поиска. Посмотрим на элемент посередине массива. Если это нуль, то первую единицу стоит искать в правой половину массива, а если единица – то в левой.

    Есть много способов писать бинарный поиск, и в его написании люди очень часто путаются. Очень удобно в данном случае воспользоваться инвариантом (это слово значит “постоянное свойство”):

    Пусть переменная left всегда указывает на (0) в массиве, а переменная right всегда указывает на (1) в массиве.

    Дальше мы будем переменные left и right постепенно сдвигать друг к другу, и в какой-то момент они станут соседними числами. Это и будет означать, что мы нашли место, где заканчиваются нули и начинаются единицы.

    Чему равны left и right изначально, когда мы ничего про массив не знаем? Первая приходящая в голову идея – поставить их на (0) и (n-1) соответственно. Увы, в общем случае это может быть неверно, потому что a[0] может быть единицей, а a[n-1] может быть нулём. Правильнее сделать вот так:

    left = -1
    right = n

    То есть изначально left и right указывают на несуществующие индексы. Но это нормально – например в массиве [1, 1, 1, 1] в конце алгоритма как раз должно быть left == -1, right == 0.

    Осталось нам написать цикл while:

    while right - left > 1:
        middle = (left + right) // 2 # именно такая формула для среднего индекса между left и right
        if a[middle] == 1:
            right = middle # right всегда должна указывать на 1
        else:
            left = middle # left всегда должна указывать на 0
    print left, right
    print a[left], a[right]
    8 9
    0 1

    Мы решили задачу для ноликов и единичек, но это легко обобщается на абсолютно любую задачу, где есть какое-то свойство, которое в начале массива не выполняется, а потом выполняется.

    Например, если мы хотим найти, есть ли число (X) в отсортированном массиве, то мы просто представим, что (0) – это числа, меньшие (X), а (1) – это числа, большие или равные (X). Тогда достаточно найти первую “единицу” и проверить, равно ли это число (X).

    a = [1, 3, 4, 10, 10, 10, 11, 80, 80, 81] # отсортированный массив
    def bin_search(a, x):
        n = len(a)
        left = -1
        right = n
        while right - left > 1:
            middle = (left + right) // 2
            if a[middle] >= x: # практически единственная строка, которая меняется от задачи к задаче
                right = middle
            else:
                left = middle
        if right != n and a[right] == x: # ответ лежит в right
            return True
        else:
            return False
    
    print (bin_search(a, 1))
    print (bin_search(a, 10))
    print (bin_search(a, 20))
    print (bin_search(a, 79))
    print (bin_search(a, 80))
    print (bin_search(a, 81)
    True
    True
    False
    False
    True
    True

    Задание

    Придумайте, как с помощью бинарного поиска решить такие задачи: * Найти первое число, равное X в отсортированном массиве, или вывести, что таких чисел нет * Найти последнее число, равное X в отсортированном массиве, или вывести, что таких чисел нет * Посчитать, сколько раз встречается число X в отсортированном массиве (в решении помогают два предыдущих пункта) * Дан массив чисел, первая часть состоит из нечетных чисел, а вторая – из четных. Найти индекс, начиная с которого все числа четные.

    Все эти задачи решаются бинарным поиском за (O(log{n})). Правда нужно понимать, что в чистом виде такую задачу решать двоичным поиском бессмысленно – ведь чтобы создать массив размера (n), уже необходимо потратить (O(n)) операций.

    Поэтому зачастую такие задачи сформулированы таким образом:

    Дан отсортированный массив размера (n). Нужно ответить на (m) запросов вида “встречается ли число (x_i) в массиве n”?

    Задание

    Найдите время работы, за которое решается эта задача?

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    Решение: Такая задача решается за (O(n + mlog{n})) – нужно создать массив за (O(n)) и (m) раз запустить бинарный поиск.

    Задание

    Решите 3 первые задачи в этом контесте:

    https://informatics.msk.ru/moodle/mod/statements/view.php?id=33216

    Бинарный поиск с вещественными числами

    У нас все еще есть функция f(x), которая сначала равна 0, а потом равна 1, и мы хотим найти это место, где она меняет значение. Но теперь аргумент функции – вещественное число. Например: * (f(x) = 1), если (x^2 > 2) * (f(x) = 0), если (x^2 leq 2)

    Понятно, что при (x = sqrt 2) (f(x) = 0), а при любом даже немного большем значении (f(x) = 1). Если мы научимся решать такую задачу, то мы научимся находить корень из двух!

    Увы, возникает проблема: действительные числа хранятся в компьютере неточно

    # известный пример
    0.1 + 0.1 + 0.1
    0.30000000000000004

    Тем более не сможем найти точное значение (sqrt 2), потому что это бесконечная непериодическая дробь. Так что давайте снова воспользуемся бинарным поиском, причем всегда (f(left) = 0), (f(right) = 1), и мы остановимся тогда, когда left и right будет очень-очень близко.

    И тут снова возникает проблема. Помимо того, что бесконечную дробь в принципе невозможно точно хранить в компьютере, ещё и арифметические операции понижают эту точность. Поэтому, чтобы явно не использовать разность между правым и левым указателем, можно задать фиксированное число шагов, которое будет выполняться.

    Так как мы знаем, что двоичный поиск работает за двоичный логарифм, можно сказать, что на угадывание десятичного разряда числа потребуется примерно три шага бинпоиска (т. к. $ $). Значит, например, если нам нужно посчитать значение функции до шести знаков после запятой, то нам нужно ещё примерно 18 шагов уже после того, как расстояние между left и right достигло одного.

    Чтобы каждый раз об этом не думать, можно считать, что ста шагов бинпоиска хватит для почти любых разумных целей.

    left = 0.0 # 0^2 < 2, а значит f(0) = 0
    right = 10.0 # 10^2 > 2, а значит f(10) = 1
    for i in range(100):
        middle = (left + right) / 2 # теперь деление не нацело, а вещественное
        if middle ** 2 > 2:
            right = middle # right всегда должна указывать на 1
        else:
            left = middle # left всегда должна указывать на 0
    print left, right
    print left ** 2, right ** 2
    1.41421356237 1.41421356237
    2.0 2.0

    Вот мы и нашли корень из 2 с достаточно высокой точностью.

    На самом деле, так можно искать ноль любой непрерывной функции (мы сейчас искали ноль функции (x^2 – 2)), у которой вы знаете значение меньше нуля и значение больше нуля.

    Задание

    Придумайте, как с помощью вещественного бинпоиска найти * (sqrt[leftroot{-2}uproot{2}17]{1000}) * какой-нибудь корень уравнения (x^4 + 3x = 5)

    Поиск максимума выпуклой функции: тернарный поиск, бинарный поиск

    Так мы только что научились находить корень непрерывной функции, у которой мы знаем значение меньше и больше 0. Но можно ли найти с помощью бинарного поиска локальный максимум функции? Можно!

    Как известно, локальный максимум функции (f) – это просто такое (x_0), что для всех близких к нему (x) значения (f(x) < f(x_0)). Для непрерывных функций выполняется более крутая вещь: слева от максимума функция возрастает, а справа от максимума функция убывает. Так это как раз отличное условие для нашего вещественного бинарного поиска!

    Если вы знаете (x_1) такое, что в его окрестности f(x) возрастает, и (x_2) такое, что в его окрестности f(x) убывает, то можно запустить между ними бинпоиск и найти точку (x_0) такую, что слева от нее возрастает значение функции, а справа – убывает. Это и есть локальный максимум.

    А если функция выпуклая, то она вообще выглядит красиво: сначала возрастает, потом максимум, потом убывает.

    Проблема только в одном: как по точке понять, в ее окрестности значение функции убывает или возрастает? Достаточно тыкнуть две точки очень-очень рядом с ней и сравнить их значения!

    Задание

    Придумайте, как с помощью вещественного бинпоиска найти * максимум функции (x – e^x) (она выпуклая, и максимум ровно один) * какой-нибудь локальный максимум функции (31x+x^3-x^4)

    Тернарный поиск

    Другой способ искать максимум – это тернарный поиск. Пусть известно, что максимум находится между left и right. Поделим отрезок на три равные части: * middle_left = (2 * left + right) / 3 * middle_right = (left + 2 * right) / 3

    Тогда если f(middle_left) < f(middle_right), то можно спокойно заменить left на middle_left (максимум точно не левее middle_left), а если f(middle_left) > f(middle_right), то можно спокойно заменить right на middle_right. Он будет работать не за двоичный логарифм, а за логарифм по основанию полтора, что больше (но асимптотически то же самое, так как отличается в константу раз).

    Оба способа работают быстро, и обобщаются на дискретный случай (то, что было в начале – когда дан массив, значения в котором сначала возрастают, а потом убывают). Но проблема есть в том, что если функция нестрого возрастает и нестрого убывает, а именно если там есть отрезки постоянства, то алгоритм не работает. В случае, когда значения функции равны, никак нельзя понять, с какой стороны искать максимум – он может быть с любой стороны.

    Задание

    Решите 4 и 5 задачи в этом контесте:

    https://informatics.msk.ru/moodle/mod/statements/view.php?id=33216

    Бинарный поиск по неотсортированному массиву

    Заметьте, что в первоначальной задаче условие на то, что сначала идут нули, а потом идут единицы несущественно. Главное, чтобы мы знали индекс, который показывает на 0, и индекс, который показывает на 1. После этого бинарным поиском мы таким же способом найдем пару соседних нуля и единицы в массиве.

    Поэтому бинарный поиск работает и не для возрастающих массивов / функций, если наша задача состоит именно в поиске двух соседних индексов, в которых условие выполняется и не выполняется.

    Например, если мы знаем, что (f(x_0) < 0) и (f(x_1) > 0), и функция непрерывная, то бинарным поиском можно найти ноль этой функции между (x_0) и (x_1), даже если функция не монотонная!

    Или, например, если нужно в массиве найти соседние четное и нечетное числа, и известно положение какого-то четного числа и какого-то нечетного числа, то это тоже можно легко сделать с помощью бинарного поиска.

    Полезно иметь это в виду, это применяется в нескольких задачах контестов.

    Бинарный поиск по ответу

    Рассмотрим такую задачу:

    Пример: “Корова в стойла”

    Условие: На прямой расположены N стойл (даны их координаты на прямой), в которые необходимо расставить K коров так, чтобы минимальное расcтояние между коровами было как можно больше. Гарантируется, что (1 < K < N).

    Решение:

    Если решать задачу в лоб, то вообще неясно что делать. Нужно решать обратную задачу: давайте предположим, что мы знаем это расстояние X, ближе которого коров ставить нельзя. Тогда сможем ли мы расставить самих коров?

    Ответ – да, можно ставить их довольно просто: самую первую ставим в самое левое стойло, это всегда выгодно. Следующие несколько стойл надо оставить пустыми, если они на расстоянии меньше X. В самое левое стойло из оставшихся надо поставить вторую корову и так далее. Даже ясно как это писать: надо идти слева направо по отсортированному массиву стойл, хранить координату последней коровы, и либо пропускать стойло, либо ставить в него новую корову.

    То есть если мы знаем расстояние X, то мы можем за O(n) проверить, можно ли расставить K коров на таком расстоянии. Ну так давайте запустим бинпоиск по X, ведь при слишком маленьком X коров точно можно расставить, а при слишком большом – нельзя, и как раз эту границу и просят найти в задаче (“как можно больше”).

    Осталось точно определить границы, то есть изначальные значения left и right. Нам точно хватит расстояния 0, так как гарантируется, что коров меньше, чем стойл. И точно не хватит расстояния max_coord – min_coord + 1, так как по условию есть хотя бы 2 коровы.

    coords = [2, 5, 7, 11, 15, 20] # координаты стойл
    k = 3 # число коров
    
    def is_correct(x): # проверяем, можно ли поставить K коров в стойла, если между коровами расстояние хотя бы x
        cows = 1
        last_cow = coords[0]
        for c in coords:
            if c - last_cow >= x:
                cows += 1
                last_cow = c
        return cows >= k
    
    left = 0 # расставить коров на расстоянии хотя бы 0 можно всегда
    right = max(coords) - min(coords) + 1 # при таком расстоянии даже 2 коровы поставить нельзя
    while right - left != 1:
        middle = (left + right) // 2
        if is_correct(middle): # проверяем, можно ли поставить K коров в стойла, если между коровами расстояние хотя бы middle
            left = middle # left всегда должна указывать на ситуацию, когда можно поставить коров
        else:
            right = middle # right всегда должна указывать на ситуацию, когда нельзя поставить коров
    print left # left - максимальное расстояние, на котором можно расставить коров в стойла
    9

    Общий принцип

    Такой метод и называется бинпоиск по ответу. Он очень важный и очень распространен на олимпиадах, очень рекомендую решать на него задачи.

    По сути мы просто взяли задачу “найдите максимальное X, такое что какое-то свойство от X выполняется” и решили её бинпоиском. Самое сложное – увидеть такую формулировку в задаче. Поэтому рассмотрим еще один пример.

    Пример: “Очень Легкая Задача”

    Условие: есть два принтера, один печатает лист раз в (x) минут, другой раз в (y) минут. За сколько минут они напечатают (N) листов? (N > 0)

    Решение: Здесь, в отличие от предыдущей задачи, кажется, существует прямое решение с формулой. Но вместо того, чтобы о нем думать, можно просто свести задачу к обратной. Давайте подумаем, как по числу минут (T) (ответу) понять, сколько листов напечатается за это время? Очень легко: [lfloorfrac{T}{x}rfloor + lfloorfrac{T}{y}rfloor]

    Ясно, что за (0) минут (N) листов распечатать нельзя, а за (xN) минут один только первый принтер успеет напечатать (N) листов. Поэтому (0) и (xN) – это подходящие первые границы для бинарного поиска.

    Примечание: заметьте, что задача в контесте немного отличается! Прочитайте внимательно условие.

    Задание

    Решите как можно больше задач в практическом контесте:

    https://informatics.msk.ru/mod/statements/view.php?id=34097

    Там могут встречаться задачи как на бинпоиск по ответу, так и на тернарный поиск по ответу.

    Михаил Федотов

    Профи

    (901)


    11 лет назад

    Есть одномерный массив – ряд чисел.
    Понадобятся две переменные целого типа: А – счётчик для перебора элементов в массиве, В – счётчик ненулевых элементов.
    Изначально А=0, В=0.
    Цикл по А:
    Перебираем элеенты массива: А=А+1,
    Если элемент массива с номером А равен 0, то увеличиваем счётчик найденных: В=В+1
    Если не дошли до последнего элемента (т. е. А меньше количества элементов в массиве) , то идём на следующий цикл.
    Ну и под конец, когда А равно числу элементов в массиве, поучаем в В – требуемое количество нулевых элементов.

    Ev

    Высший разум

    (118682)


    11 лет назад

    Создаем массив из N элементов.
    Заполняем его целыми числами.
    Создаем переменную целого типа, которая будет содержать число найденных нулевых элементов массива.
    Создаем переменную целого типа – счетчик для цикла.
    В цикле от 0 до верхней границы массива, перебираем элементы массива и если элемент равен нулю,
    то увеличиваем на единицу переменную, которая содержит число найденных нулевых элементов массива.
    По окончании цикла выводим результат – число, которое хранится в переменной.
    Все…

    tsb

    Мастер

    (2465)


    11 лет назад

    #include …
    #define N 50

    int i, NumZero=0, DIM[N];

    int main() {

    for(i=0;i<n;i++)> = -30+rand() % 100; // как то заполняем массив

    //а вот теперь ищем нулевые элементы
    //
    for(i=0;i<n;i++)>==0) NumZero++;

    cout << “Количество нулевых элементов= ” << NumZero << endl;

    return(0);
    }

    Добавить комментарий