Задать вопрос
  • Почему не работает алгоритм Брона-Кербоша?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    У вас две большие ошибки с языком питон. Вы в цикле в alg итерируетесь по списку add, меняя его. Так делать нельзя. Ломаются итераторы и вы пропускаете половину элементов.

    Вместо этого используйте цикл while:

    while add:
      item = add[0]
      add.remove(item)
      s.append(item)
    ...


    Вторая большая ошибка, в питоне списки передаются по ссылке! Поэтому s, который у вас параметр рекурсивной функции, на каждом уровне один и тот же список. Но при выводе множества вы сразу же возвращаетесь из функции, не удаляя item из s. Вам надо чтобы все изменения в s были отменены по выходу их функции. Перед return делайте s.remove(item).

    С этими двумя изменениями должно работать. Ну, и у вас главная оптимизация алгоритма не реализована. Надо в начале цикла проверить, что среди оставшихся вершин в add есть хоть одна соединенная с каждой вершиной из exists. Если таковых нет - нужно вывалится из цикла.

    Еще по коду:
    range(n) в python возвращает 0..5. У вас там цикл по ребрам в __main__ и там идет обращение к i-1 в массиве graf. Т.е. обращение к [-1]. Вряд ли вы это хотели. Работает по чистой случайности, потому что в питоне a[-1] дает последний элемент.

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

    Вместо функции check(), вы сделайте нормальный список смежности один раз, а то просто глаз режет от неоптимальности.
    neighbors = [[] for i in range(count_vershin)]
    for edge in graf:
      neighbors[edge[0]-1].append(edge[1]-1)
      neighbors[edge[1]-1].append(edge[0]-1)
    Ответ написан
  • Как ускорить программу?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Надо использовать более умный и быстрый алгоритм. Вы после каждой помены за O(n) проверяете, что массив отсортирован. когда как можно делать это за O(1) - ведь массив меняется всего в двух местах.

    Для понимания алгоритма, сначала сделаем немного другую версию за O(n), а потом соптимизируем до константы.
    Вы вместо сравнения с сортированным массивом пройдитесь по массиву и смотрите, что для всех соседних элементов следующий более или равен предыдущему.
    Следующий шаг - заведите массив bool на n-1 элемент и храните в нем пометки, что в данной позиции массив отсортирован. При помене местами двух элементов надо пересчитать 4 позиции в этом массиве. Потом пройдитесь по массиву и проверьте, что там все элементы true. Пока все еще работает за линию и еще хранит лишний массив.
    А теперь последний шаг - можно не проходиться по массиву bool каждый раз, если поддерживать счетчик, сколько там истинных элементов. После ввода массива list надо будет заполнить массив bool-ов и подсчитать, сколько там true. При перестановке a и b у вас максимум 4 элемента в массиве bool могут поменяться. Вот при этих изменениях вы счетчик пересчитывайте. Просто тупо при каждой записи в этот массив вычтите 1 из счетчика, если текущее там значение true и прибавьте 1, если новое значение true.
    Ответ написан
    Комментировать
  • Нужна помощь в языке С, касательно матриц, возможно циклов -?

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

    Можно писать так:
    if ((i > 0 && a[i-1][j] > a[i][j]) || (i < n-1 &&  a[i+1][j] > a[i][j])) {
      // текущий элемент меньше хотя бы одного соседа в том же столбце.
    }

    еще можно так:
    if ((i == 0 || a[i-1][j] > a[i][j]) && (i == n-1 ||  a[i+1][j] > a[i][j])) {
      // текущий элемент меньше всех соседей в том же столбце.
    }



    Комбинируя условия на циклы через || и && можно составить любое нужное вам условие, которое смотрит только на существующих соседей. Но тут важен порядок операций. В C++ (да и почти везде, на самом деле) условия выполняются слева направо и прекращают выполнение, как только результат становится известен. Вот, в первом примере сначала идет проверка на i>0 а потом обращение к массиву через логическое И. Поэтому, если программа будет обрабатывать первый элемент в строке уже на первом условии она заметит, что все условие обязательно false, ведь там стоит &&. И условие с обращением к массиву не будет произведено никогда.
    Ответ написан
    Комментировать
  • В чём разница в скорости работы между перебором всех состояний игры и функций Шпрага-Гранди?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Просто так примененная в лоб функция Гранди нисколько не быстрее обычных пометок "выигрышная"/"проигрышная" ситуация. Даже медленнее, потому что надо не просто смотреть, есть ли переход в выигрышную ситуацию, а надо смотреть в какие значения можно сделать переход и найти минимальное ими не покрытое.

    Но она обладает замечательным свойством - если состояние игры можно разложить на несколько независимых игр и игорк в свой ход может сделать ход в любой из игр, то функция Гранди может быть подсчитана как xor значений для всех состояний. По умному это называется, что игра является суммой игр. В некоторых задачах это позволяет колоссально сократить простарнство состояний.

    Один пример - игра Ним. Есть несколько кучек камней. За свой ход игрок может взять сколько угодно камней из любой кучки. Проигрывает тот, кому не останется камней. В простом переборе вам придется в качестве состояния хранить вектор количеств камней в каждой кучке. Но ведь тут стостяние очевидно раскладвается на под-игры: каждая кучка - своя отдельная игра. Причем, функция Гранди тут тривиальна - это просто количество камней. Вот и получается решение игры Ним - взять xor размеров всех кучек. Если не 0, то состояние выгрышное. Надо взять из какой-то кучки столько камней, чтобы получился xor, равный 0.

    Еще пример - есть плитка шоколада. Игроки ее ломают вдоль клеток. За свой ход игрок может взять любой прямоугольный кусок и разломить его как-то вдоль клеток (если там более 1x1 клеток, конечно). Проигрывает тот, кто не сможет сделать ни одного хода. Опять же, при простом переборе пришлось бы хранить в состоянии размеры всех кусоков. Тут ОЧЕНЬ много вариантов. А с функцией Гранди - достаточно рассмотреть состояния вида "одна плитка размера n x m". После одного хода у вас будет 2 плитки, но меньшего размера. Вы уже знаете для них функцию гранди, XOR'ите их и получаете функцию для возможного перехода.
    Ответ написан
    4 комментария
  • Когда использовать ООП?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    ООП - это не только, когда вы берете какие-то сущности из предметной области и оборачиваете каждую в объект, который что-то умеет делать. Это больше подход к организации кода. Вы делите задачу на подзадачи, а данные на обособленные части, абстрагируете детали внутри объектов. Это позволяет снижать сложность архитектуры. Теоретически любую программу можно написать внутри одной огромной функции с кучей goto. Но так никто не делает, потому что это невозможно поддерживать и невероятно сложно написать. ООП - это логическое продолжение процедур. Теперь вы не только какие-то части программы абстрагируете в одном месте, но теперь еще и данные вмести с ними.

    Мне нужен объект, который будет хранить состояние/данные, и есть общие операции над этим состоянием?


    Вопрос: что значит нужен? Всегда можно взять глобальную переменную, написать функции, которые это состояние принимают и что-то с ним делают. Но довольно часто организация в виде объекта просто удобнее.
    Ответ написан
    1 комментарий
  • Как получить всевозможные комбинации массива PHP?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Подсчитайте руками, сколько комбинаций для массива из 1,2,3,4 элементов? ответ будет 1,3,7,15 - степени двойки минус один. Подумайте, откуда это берется? Каждый элемент может или быть в ответе, или нет. 2 варианта. Премножаем все, получаем степень двойки. Но это учитывает варинат пустого ответа, поэтому один надо вычесть.

    Тут есть 2 подхода - рекурсивная функция перебора, которая или берет или пропускает текущий элемент. В конце, если хоть один элемент был взят - выводит текущий массив.

    Второй подход - перебирать битовую маску. Каждое число от 1 до 2^n будет в двоичной системе числения представлять какой-то вариант выбора разрядов. Поэтому можно прогнать цикл от 1 до 2^n, потом посмотреть на биты этого числа и брать соотвествтующие числа из массива.
    Ответ написан
    Комментировать
  • Как перенести строку в двумерном массиве, если количество элементов больше определенного количества?

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

    Самое простое решение - в цикле вывода символов выводить '\n', если текущий символ 20-ый, 40-ой и т.д. Например, можно проверять (i+1) % 20 == 0.
    Ответ написан
    Комментировать
  • Какой алгоритм решения данной задачи?

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

    Вещественные числа в Си, например - это тип float. Корень - sqrt(). Пи - M_PI. В формуле есть e в степени чего-то там. Почти в любом языке уже есть функция exp().

    Все решение - читаете 2 числа, потом присваивайте вещественной переменной один, деленное на второе число, помноженное на sqrt() от двух, умноженного на Пи. Это все умноженное на exp() от первой переменной, домноженной на себя, поделенной на 2 и на вторую переменную в квадрате.
    Ответ написан
    1 комментарий
  • Содержит ли массив определенные числа?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Вариант простой - перебрать все участки 3x3 (смотрите левый верхний угол - это 2 цикла от 0 до width-3 и от 0 до height-3) и проверить, что они хорошие.

    Для проверки можно или 1) проверить, что все числа от 1 до 9 и разные, или 2) проверить, что там ровно один раз каждая из цифр встречается. Второй вариант короче и быстрее, но для него вам придется завести массив счетчиков от 0 до 9. Перед каждой проверкой он занулен. Обойдите ваш 3x3 блок и увеличьте соответствующий счетчик (что-то типа count[a[beg_x+i][beg_y+j]]++). Только не забудьте проверить, что число от 1 до 9, чтобы не вылезать за границы массива. Потом пройдитесь по массиву счетчиков циклом от 1 до 9 и проверьте, что там везде стоят единицы.
    Ответ написан
    Комментировать
  • Как решить эту комбинаторную задачу?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Представьте вашу строку, и нарисуйте палочки между двумя соседними битами там, где проходит граница блоков. Вопрос в задаче подсчитать количество способов расставить эти палочки правильно (стоит обязательно палочка в самом начале и самом конце, между любыми соседними палочками - ровно одна единица).

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

    Вот и считайте, сколько вариантов поставить палку перед каждой единицей. Надо подсчитать расстояния между каждой парой единиц и перемножить. Если единиц вообще нет - ответ 0, если одна - то 1.

    Можно сделать за один проход, если запоминать, где стояла предыдущая единица.

    int64_t CountWaysToSplit(std::string s) {
      int last_one = -1;
      int64_t res = 1;
      for (int i = 0; i < s.length(); ++i) {
        if (s[i] == '1') {
          if (last_one >= 0) res *= i - last_one;
          last_one = i;
        }
      }
      return last_one >= 0 ? res : 0;
    }
    Ответ написан
    Комментировать
  • Как реализовать калькулятор со скобками на си(через обратную польскую запись)?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Алгоритм сортировочной станции. Можно переключиться на английскую версию вики, там есть детальный пример, и псевдокод.

    Если же от вас требуется что-то более самостоятельное, то делайте так - найдите в строке операцию с наименьшим приоритетом (ту, которая будет выполнена последней). Рекурсивно выведите обратную польскую запись для левой и правой половины, потом выводите/выполняйте операцию.

    Чтобы найти операцию - проходитесь по строке, считая сколько сейчас открыто скобок. Запомните позицию самого правого встреченного "+"/"-" и "*"/"/". Если ничего не нашли - значит можно откусить внешнюю пару скобок (если скобок нет, то вся строка - число). Иначе, если есть + или - - это та самая последняя операция. Если таковой нет - то будет * или / - берите ее.

    Это работает за квадрат в отличии от алгоритма по ссылке, но зато пишется элементарно.
    Ответ написан
    2 комментария
  • Не понимаю в чём ошибка, можете помочь?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    У вас $rank равен 8, когда как допустимые значения 0..7. И вы обращаетесь по недопустимуому индексу в массиве, в котором 8 элементов.
    Ответ написан
    Комментировать
  • Как найти максимум среди всех локальных минимумов заданной матрицы?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Алгоритм тривиальный - перебрать все клетки матрицы двумя вложенными циклами. Проверить, что текущая клетка - локальный минимум (сравнить со всеми соседями). Если да, то надо сравнить ее с текущим кандидатом на максимум.

    Фактически, тут 2 вложенные задачи: 1) Проверить, является ли значение локальным минимумом 2) Найти максимум в матрице (возможно игнорируя какие-то клетки).

    Советую решить задачи раздельно с помощью функций.

    Для первой задачи напишите функцию.

    bool IsLocalMinumum(int a[][m], int n, int m, int i, int j);


    Функция должна перебрать 4 варианта (прибавить/вычесть 1 из первого или второго индекса) и для каждого проверить: 1) сосед есть, т.е. не вышел за границу, 2) значение соседа меньше текущего. Если оба условия выполнились для какого-то направления - вы нашли "плохого" соседа. Текущая клетка не может быть минимумом. Сразу же возвращайте false. В конце функции всегда возвращайте true.

    Вторая задача - тривиальна совсем. Можете выбрать максимум из просто одномерного массива? Теперь вместо одного цикла по массиву делайте 2 вложенных по матрице. Потом допишите туда условие, вызывающее вашу IsLocalMinimum. Если вершине не минимум - просто делайте пропускайте клетку. Буквально if (!IsLocalMinimum(a, n, m, i, j)) continue;
    Ответ написан
    Комментировать
  • Как найти кратчайший путь с минимальным количеством поворотов(повороты в приоритете)?

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

    Во-первых, все расстояния в графе будут не одно число, а пара - собственно, длина пути и количество поворотов. При суммировании двух длин складывайте их покомпонентно, при сравнении сравнивайте сначала длины, а потом количество поворотов. Теоретически, можно схлопнуть все назад в одно число, если считать 100000*<длина> + <количество поворотов>. Но могут быть спецэффекты, если пути очень сложные и имеюют более 100000 поворотов. Еще, опасайтесь переполнения.

    Для каждой клетки сделайте 4 вершины, которые будут означать, что вы находитесь в этой клетке и "смотрите" в одну из 4-х сторон.

    Создайте 4 ребера между соседними направлениями с длиной {0, 1} (длина 0, но 1 поворот). От каждой вершины создайте также ребро длинной {1, 0} (длина - 1, 0 поворотов) в вершину в соседней клетке с тем же направлением (от "верхней" из 4-х вершин сделайте ребро в "верхнюю" же вершину в клетке сверху. Аналогично по трем остальным направлениям).

    Теперь кратчайший путь в этом графе будет то - что вам надо. Есть еще вопрос с начальными конечным направлением. Можно добавить в начальную и конечную клетки "центральную" вершину, в которая связанна ребрами длины {0, 1} (Важно сделать 1 поворот, иначе можно будет крутиться на месте через эту центральную вершину). Считайте, что эта вершина - "смотреть в пол". Вы начинаете в какой-то клетке, смотря в пол, и вам надо оказаться в другой клетке, опять же смотря в пол. Вы можете в клетке повернуться, или перейти в клетку впереди вас.

    Поскольку тут уже разные длины ребер, обход в ширину уже не будет искать кратчайшее расстояние. Но можно использовать дейкстру/A*.

    При реализации не надо даже хранить все вершины и ребра. Просто у вас теперь вершина вместо {x, y} будет описываться {x, y, d}. Вместо перебора 4-х направлений [-1,0],[0,-1],[0,1],[1,0] - вы делаете переход {x+dx[d],y+dy[d],d} или меняете d на (d+1)%d и (d+3)%4. И в очередях вы сохраняете не одно число, а пару {length, turns}. Для A* нужно еще прикидывать оставшуюся длину - ну вы по длине берите, что у вас уже есть, а по количеству поворотов берите 1, если у начала и конца разные направления.

    Играясь с длинами ребер и их сравнениями вы можете, например, разрешать чуть более длинные пути, если в них сильно меньше поворотов. (например, можно обойти лабиринт по периметру, чем чуть короче но зиг-загами ходить внутри). Для этого длина ребра может быть length*K+turns.

    Поскольку тут в 4-5 раз больше вершин, это будет работать в 4-5 раз медленнее.
    Ответ написан
    Комментировать
  • Как считывать несколько переменных в языке C?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Я правильно понимаю, что пользователь вводит от 1 до 3 чисел в строке и ожидает, что программа эти числа обработает и выведет результат? Иными, словами, программа реагирует на клавишу enter?

    Можно читать символы по одному через getch(), пока не прочитаете '\n'. По пути нужно парсить числа. Если символ цифра - то умножйте текущее число на 10 и прибавляйте цифру (помните, что (int)'0' != 0. Но символы цифры, слава богу, идут подряд в алфавите). Если прочитали пробел - переходите к следующему числу. Или, чтобы обрабатывать всякие много пробелов, лишние символы и другие проблемы ввода - лучше прочитать сразу же всю строку через fgets() и потом в ней уже парсить числа.
    Ответ написан
  • Как мне задать десятичкную дробь в моем коде?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вы используете тип int. Для дробей нужно вещественное число float или double.

    Выводить и читать его надо не через "%i", а через "%f" или "%lf".
    Ответ написан
  • Как создать программу перевода из 8-ричной СС в 10-ричную?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Как вам уже написали - надо складывать цифры, умноженные на степени 8.

    Это один цикл, с последней цифры до первой: Прибавляйте к ответу текущую цифру, умноженную на текущую степень и потом домножайте текущую степень на 8.
    Ответ написан
    Комментировать
  • Как сохранить графическое изображение, нарисованное в Pascal ABC с помощью модуля GraphABC?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Похоже, можно воспользоваться методом Create(r: System.Drawing.Rectangle) - судя по описанию - это то, что вам нужно, но я не уверен. Picture же можно сохранять методом Save.

    Если не работает, то можно воспользоваться этим примером. Создайте Picture и рисуйте туда параллельно с рисованием на экран.
    Ответ написан
    Комментировать
  • Как исправить ошибку invalid controlling predicate?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    OpenMP работает только со стандартными циклами. У вас же инициализация по i, условие по x, итерация i++. OpenMP просто не умеет понять, сколько там итераций и как их можно распаралелить вообще.

    Вам надо сделать так:
    const int num_iterations=1000000;
    const double h = (b-a)/num_iterations/2.0
    x = a;
    #pragma  omp  parallel  for  reduction (+:S)
    for (int i = 0; i < maxi; i++)
    {
        S = S + 1*(x/(x+1));
        x = x + h;
        S = S + 4*(x/(x+1));
        x = x + h;
        S = S + 1*(x/(x+1));
    }


    Удобнее, если зафиксировать сначала количество итераций, а не шаг h (шаг отсюда вычисляется). Потому что если отрезок нацело не делиться на h, то надо как-то это обрабатывать и вообще, а можно ли так считать?

    Я добавил reduction (+:S) в инструкцию openMP, потому что вам надо подсчитать общую сумму же. Без этого каждый поток что-то насуммирует отдельно. Шарить S между потоками сложно - может быть data race.
    Ответ написан
    Комментировать
  • Где и для чего используют кучу (heap)?

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

    Детали:
    - Операция поиска минимума работает за константу, а не за логарифм. Но это мелочь, потому что чаще всего после поиска происходит удаление минимума, или добавление нового элемента, которые работают за логарифм.
    - Эта структура данных сильно проще сбалансированного дерева поиска и поэтому работает быстрее. Тупо на поддержку структуры надо тратить меньше сил. Проще поменять местами 2 элемента массива, чем крутить красно-черное дерево.
    - Куча требует меньше памяти. Тут нужны только сами элементы в массиве. Всякие деревья поиска требуют указателей на детей, какие-то дополнительные данные, вроде цвета вершины.
    - Куча всегда идеально сбалансирована. Деревья поиска же обычно могут быть раза в 2 выше, чем идеальный логарифм.
    - Кучу можно поддерживать в массиве. Это сильно дружественнее к кэшу процессора, чем разбросанные вершины дерева с указателями друг на друга. Поэтому куча, мало того, что выполняет меньше операций, эти операции сами происходят быстрее.
    Ответ написан
    4 комментария