Задать вопрос
  • Можно ли обратиться к полю дочернего класса через указатель на базовый?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Если вы точно знаете, что это именно экземпляр дочернего класса, то можно скастовать к дочернему через dynamic_cast, или вернуть скастованный указатель на дочерний класс через какой-нибудь виртуальный метод Derived* GetDerived(). Без какого-либо преобразования к дочернему классу - не получится. Не упоминать этот дочерний класс - тоже. Только если вы это поле/метод выведите в виртуальный метод базового класса.
    Ответ написан
    Комментировать
  • Как находить лучший способ решение задачи на олимпиадном программировании?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Нарешаете много задач, научитесь в голове перебирать известные алгоритмы.

    А так - на до сначала построить математическую модель задачи. Понять, на что эта модель похожа. Может, тут граф нарисовать можно? Какие алгоритмы на графы вы вообще знаете? Применимы ли они тут? Или это строка. Какие есть алгоритмы на строки? Смотрите еще ограничения. По ним обчно понятно, что тут нужно что-то за O(n), или за O(n log n) написать. Множество применымых алгоритмов еще больше уменьшается.

    В задачах на оптимальность чего-то помогает рассуждение "рассмотрим ответ. Какие у него можно заметить свойства. Можно ли какой-то ответ немного поменять, не ухудшая его?" Так вы получите какое-то свойство, которое можно уже применять для построения ответа. Например в задачах на жадность так можно понять, что надо отсрортировать что-то.

    Потом, очень частая тема - ДП. Попробуйте параметризировать задачу. Свести ее к подзадачам. Формализуйте ДП (считаем вот такую вот функцию от вот этих вот параметров вот с таким вот физическим смыслом).
    Ответ написан
    Комментировать
  • По какому алгоритму находить наибольшую общую подпоследовательность двух дублирующихся слов?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Вообще, вот алгоритм для произвольных слов: https://e-maxx.ru/algo/longest_increasing_subseq_log

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

    Какие у вас там ограничения: длины строк и количество повторений?
    Ответ написан
  • Какие темы самые важные для олимпиадного программировании в книге Кормена «Алгоритмы. Построение и анализ»?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Часть III. Структуры данных
    Часть VI. Алгоритмы для работы с графами

    Менее важные, но встречающиеся темы:
    Глава 30. Полиномы и быстрое преобразование Фурье
    Глава 31. Теоретико-числовые алгоритмы
    Глава 32. Поиск подстрок
    Глава 33. Вычислительная геометрия

    Но лучше прочитать, таки, всю книгу. Вправляет мозги, учит строить математические модели задач, что в олимпиадах очень важно.
    Ответ написан
    6 комментариев
  • Как вернуть до пяти типов из одной функции?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Надо возвращать указатель на базлвый класс. Ну и, один из 5 экземпляров надо создавать через new.
    Ответ написан
    8 комментариев
  • Нужно ли здесь выравнивание на стеке?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Раз вы просто копируете через memcpy, то никаких проблем не должно быть.
    А еще, вы уверены что std::swap сам эту оптимизацию уже не применяет? Зачем вы какие-то свои велосипеды строите?
    Ответ написан
  • Как вычислить число итераций алгоритма Евклида через вычитание?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Алгоритм через деление с остатоком - это всего-лишь оптимизация алгоритма через вычитание. Просто куча вычитаний делается скопом. Так же можно их все и подсчитать. Просто прибавляйте каждый раз не 1, а сколько там раз меньшее число в большее помещается. Типа answer += b/a
    Ответ написан
  • Нужно ли переводить из градусов в радианы для правильного направления стрелки?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Да, нужно перевести скорость в радианы. Так, чтобы 0 было pi/2 радиан (направление вверх), а -200 - это 4/3pi. И радианы при увеличении скорости уменьшаются.

    Поскольку все линейно, то фрмула такая: alpha = pi/2 - speed*5/1200.0

    Что такое angleValueTransformer я не знаю, но если ему задаются отрезки углов и значений, которые оно линейно преобразует в друг друга, то углы должны быть от 240 до -60, что соответствует скоростям от -200 до 200. Ну, или углы от 4/3pi до -pi/3, если вы значение сразу в синусы/косинусы передаете, не переводя из градусов в радианы.

    И, кстати, развертка будет не 310, а 300 градусов от -200 до 200. Иначе скорость 0 не будет вертикально вверх.
    Ответ написан
  • Как найти все способы представить число в виде произведения чисел Фибоначчи?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Я бы рекурсивно решал, перебирая все числа фиббоначи. Если делится - запускаемся рекурсивно от частного. Суммируем ответы для всех вариантов делителя.

    Что бы не перебирать разные перестановки множителей (а они в ответе, судя по примерам, не нужны), я бы передавал в функцию еще и максимальное разрешенное число фиббоначи. Изначально разрешены все числа, но при делении на какое-то число его же и передаем дальше. Таким образом функция будет перебирать только не возрастающие последовательности делителей.

    Можно будет чуть ускорить, используя какой-нибудь ассоциативный массив для запоминания ответов. Его можно даже переиспользовать среди всех тестов.
    Ответ написан
    5 комментариев
  • Bitmap, hBitmap как загрузить сохраненный bmp?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Надо воспользоваться Gdiplus::Bitmap. Он умеет читать из файла.
    Дальше из него можно получить HBITMAP, если надо. А можно и дальше Gdiplus::Bitmap передавать.

    HBITMAP LoadHbitmapFromFile(const std::wstring& filename)
    {
        Gdiplus::Bitmap* bitmap = Gdiplus::Bitmap::FromFile(filename.c_str(), false);
        HBITMAP result = NULL;
        if (bitmap)
        {
            bitmap->GetHBITMAP(Gdiplus::Color(255, 255, 255), &result);
            delete bitmap;
        }
        return result;
    }
    Ответ написан
    5 комментариев
  • Не работает простой код хотя он правильный в чем может быть проблема?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вы уверены? У меня точно такой же код выводит числа от 1 до 10. Скорее всего вы программу нескомпилировали, или запускаете какой-то другой код по ошибке.
    Ответ написан
    1 комментарий
  • Является ли безопасным отнять от указателя 1 и итерироваться по массиву [1,N], а не [0, N-1]?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Это не безопасно. Это не UB, но оно не определено стандартом. А значит, компилятор может что-то там соптимизитровать и поломать код.
    В документации написано:

    Every value of pointer type is one of the following:
    * a pointer to an object or function (in which case the pointer is said to point to the object or function), or
    * a pointer past the end of an object, or
    * the null pointer value for that type, or
    * an invalid pointer value.


    Т.е. arr у вас, вообще-то, invalid pointer value перед циклом.

    Плюс там же написано:
    Any other use of an invalid pointer value has implementation-defined behavior.


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

    Edit: немного не то написал. Так делать "не рекомендуется", а не "небезопасно". Оно генерирует непереносимый код. Если у вас на конкретном компиляторе с конкретными ключами работает, то, в общем-то, можно использовать. Но лучше не надо.
    Ответ написан
    5 комментариев
  • Vector не обновляется?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    (Исходя из обсуждения в комментариях)
    Возможно, проблема с параллельным кодом. Какой-то код где-то изменяет вектор. Если не очевидно, где оно все перезаписывается, то помните, что добавление элементов вектор может вызвать увеличение внутреннего буфера и копирование всех элементов. Поэтому даже какой-то код, просто добавляющий новые элементы в вектор, может вызвать наблюдаемый эффект необъяснимой перезаписи объекта в векторе на предыдущее значение. Вообще, надо бы защищать вектор от параллельного доступа разными потоками всякими мютексами и критическими секциями. Ну нет, так поменяйте его хотя бы на list. тогда добавление новых элементов и изменение значений где-то не будут вызывать data race. Но тогда надо алгоритмы менять, ибо обращение по индексу - долгая операция.
    Ответ написан
    Комментировать
  • Сколько всего паролей будет?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Если пароли считаются уникальные, как строки, то ответ - 2^n: любую строку из "a" и "b" можно как-то составить.

    Если пароли считаются уникальные, как последовательность "aa", "a", "b" и "bb" (так, для трех букв отдельно подсчитаются a+aa, aa+a и a+a+a), то ответ можно найти по рекуррентной формуле F(N) = 2(F(N-1)+F(N-2)), F(1) = 2, F(2) = 6.

    Формла выводится так: В конце может быть один символ. Тут 2 варианта - это "a" или "b", тогда получается F(N-1) последовательностей длины N-1. Если же в конце идет "aa" или "bb", то таких последовательностей ровно F(N-2). Просуммировав все получится 2(F(N-1)+F(N-2))

    Это что-то вроде чисел фиббоначи. Можно еще вывести формулу:
    F(N) = (1+sqrt(3))^(N+1)-(1-sqrt(3))^(N+1) / (2sqrt(3))


    Если пишите программу, то лучше считать F() итеративно. Только учтите, там будут очень большие числа - в 64-битный тип не поместится.
    Ответ написан
    Комментировать
  • Какова сложность алгоритма поиска всех элементов AVL дерева, принадлежащих интервалу?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Оценка O(log n + m) - правильная. Ее нельзя (или очень сложно) вывести рекуррентным соотношением, потому что она сильно завязана на условия в функции. Там 2 рекурсивных вызова, но они происходят не всегда.

    Оценку можно вывести посчитав различные вызовы функции. Сначала, когда node->key не в интервале - будет ровно один рекурсивный вызов. Ну, потому что не в интервале, это или <min, или >max. Т.е. одно из двух условий (node->key > min, node->key < max) точно нарушается.

    Поэтому сколько-то уровней дерева будет ровно один вызов. Потом будут вызовы в вершины внутри интервала, и могут быть вызовы за границы интервала. Первые все посчитаются в слагаемом M в оценке. Сколько будет вторых? Не более 2*количество уровней - максимум один вызов в числа меньшие min и один в числа, большие max.

    Вот мы оказались в какой-то вершине в отрезке (A). И пошли от нее влево в вершину вне отрзка (B). А так же вправо в вершину (C). Так вот, все ключи в поддереве С не меньше A, а значит, ни одна из них не будет "торчать" из отрезка влево. Поэтому, "плохие" вершины вне отрезка слева, которые мы посещаем, там не встретятся никогда. Значит, на следующем уровне может быть максимум одна "плохая" вершина слева - правый ребенок B. И так далее, пока мы не спустимся до вершины опять в отрезке. Аналогичное рассуждение показывает, что может быть только по одной плохой вершины справа на каждом уровне.
    Ответ написан
    Комментировать
  • Почему создается массив с мусором?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Закрывающего нуля в конце строки нет. Поэтому вывод лезет за границу выделенной памяти, а там мусор.

    И еще, очень глаз режет: правильно писать "symbol", а не "simbol".
    Ответ написан
  • Почему ближайшие точки определяются неправильно?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Есть еще вот такое решение. Выводится так: задаем обе прямые параметрически (точка + t или u, помноженная на вектор вдоль прямой). Получаем выражение для квадрата расстояния между точками как функцию от t и u. Ищем ее минимум, приравняв к 0 частичные производные. Там получаются 2 линейных уравнения.

    Vector3 a = axis2.first - axis1.first;
    Vector3 v1 = axis1.second - axis1.first;
    Vector3 v2 = axis2.second - axis2.first;
    float v11 = Vector3::DotProduct(v1, v1);
    float v12 = Vector3::DotProduct(v1, v2);
    float v22 = Vector3::DotProduct(v2, v2);
    float av1 = Vector3::DotProduct(a, v1);
    float av2 = Vector3::DotProduct(a, v2);
    // Решаем систему методом Крамера:
    // t*v11-u*v12=av1
    // t*v12-u*v22=av2
    float d1 = -av1*v22+v12*av2;
    float d2 = v11*av2-v22*av1;
    float d = -v11*v22+v12*v12;
    float t = d1/d;
    float u = d2/d;
    point1 = axis1.first + v1 * t;
    point2 = axis2.first + v2 * u;
    Ответ написан
    Комментировать
  • Как получить все возможные комбинации?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    itertools.combinations. Скармливаете туда range индексов вашей строки. Получаете список из 1/2/сколько надо сделать замен индексов, на эти позиции ставьте ваши Z, Y.

    Вот набросок кода.
    Я не питонист, возможно есть более лаконичная реализация. Особенно мерзкий хак с перобразованием строки в list, потому что строки в питоне immutable.

    import itertools
    
    def GenerateAll(source, rep):
      for comb in itertools.combinations(range(len(source)), len(rep)):
        s = list(source)
        for (i, j) in enumerate(comb):
          s[j] = rep[i];
        yield "".join(s)
        
    print(list(GenerateAll("abcd", "XY")))
    # ['XYcd', 'XbYd', 'XbcY', 'aXYd', 'aXcY', 'abXY']
    Ответ написан
  • Как сделать побитовое умножение и сложение большого числа decimal?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вот эти побитовые сдвиги работли бы, если бы вы хранили число в двоичной системе счисления. Допустим, по 16 бит в каждом int. В таком виде вычисления побыстрее будут немного, но при выводе надо переводить число в 10-ую систему счисления. Судя по примеру ответа, вы храните по 3 десятичных знака в каждой ячейке. Т.е. у вас основание системы счисления 1000. И тут нужны сдвиги не битовые, а тысячные (которых не существует).

    Вообще, основной цикл умножения должен быть примерно такой:

    for (i = 0; i < len1; ++i)
      for (j = 0; j < len2; ++j) 
        c[i+j] += a[i]*b[j];


    И потом надо сделать все переносы. Вот тут и происходят сдвиг на i позиций (в системе счисления 1000) - это то самое +i в индексе у массива результата. И вместо выполнения прибавления, когда бит равен 1, тут происходит умножение на a[i]. В битовом случае оно как раз вырождается в умножение на 1 или 0 - прибавление или пропуск прибавления.

    Это работает даже если вы в массивах a,b храните по несколько бит в каждой ячейке, или даже десятичных знаков. Это просто умножение в столбик в произвольной системе счисления.

    Правда, тут проблема, что вот такое вот умножение, если вы храните по 32 бита, оно переполнится, и ответ надо в 64-битном типе получать. И даже если вы храните по 16 бит, то сумма может переполниться из-за большого количества слагаемых. Ну это решается выполнением переноса сразу же:
    const int BASE = 1000; // База системы счисления. Оно же максимальное число в ячейке +1.
    for (i = 0; i < len1; ++i) {
      int carry = 0;
      for (j = 0; j < len2; ++j) {
        carry += c[i+j] + a[i]*b[j];
        c[i+j] = carry % BASE; 
        carry /= BASE;
      }
      int i = len1+len2-1;
      while (carry > 0) {
        carry += c[i];
        c[i] = carry % BASE;
        carry /= BASE;
        ++i;
      }
    }
    Ответ написан
  • Нужно ли делать защиту при делении на ноль?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Для облегчения отладки в будущем, стоит вставить assert какой-нибудь. Чтобы программа падала с понятной ошибкой, при попытке поделить на 0.
    Ответ написан
    Комментировать