• Какой алгоритм использовать для подбора ингредиентов по составу?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Как выяснили в комментариях, метрика будет sum_i w[i]^2 (actual[i]/ needed[i] - 1) ^ 2.

    пусть x[i] - соклько i-ого продукта
    Пусть a[i][j] - сколько в еденице i-ого продукта j-ого ингредиента. Тогда всего j-ого ингредиента будет sum_i a[i][j] x[i].

    Тогда получаем следующую оптимизационную задачу:

    Sum_j (sum_i A'[i][j] x[i] - C[i]) ^ 2 -> min
    sum_i x_i = 1
    x_i >= 0

    Сумма всего приравнивается к 1, потому что иначе надо считать пропорции и получится вообще ужас-ужас.

    A'[i][j], C[i] - какие-то числа, которые элементарно получаются из a[i][j], w[i] и needed[i].
    A'[i][j] = a[i][j]*w[j]/needed[j]
    C[j] = w[j] / needed[j];

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

    Можно применить метод лагранжа, а точнее метод Каруша — Куна — Таккера

    Сначала выразите x_1 = 1 -sum_j>1 x_j, подставьте во все уравнения и получите:
    Sum_j (sum_i A''[i][j] x[i] - C'[i]) ^ 2 -> min
    sum_i x_i -1 <= 0
    x_i >= 0

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

    Для условия жесткости придется рассмотреть 2 случая - лямбда - 0 или тогда sum_i x_i - 1 = 0

    Прорешать оба случая (СЛАУ) и взять те, где все получается положительным.

    Это что-то близкое к методу наименьших квадратов, но тут вводятся дополнительные переменные, чтобы боротся с ограничениями неотрицательности и общей суммы.
    Ответ написан
  • Как сравнить unsigned long long и char* в C++?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Потому что вы передаете во второй to_string первый символ (его код). Если вы передадите в качестве числа 61, а в качестве строки "A..." то будет true.

    Если вам надо сравнить число и строку, как набор цифр, то вы или только число переводите в to_string или только строку переводите в to_number.
    Ответ написан
    Комментировать
  • Как обращаться к элементам массива через указатель?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Раз вы передаете ссылку, то tmp_word и s являются тупо массивами, т.е. указателями на char. Поэтому вам не надо одновременно их разыменовывать и обращатся по индексу. Или пишите *(tmp_word+tmp) или tmp_word[tmp]

    А вообще, можно их и не передавать как ссылки а передвать сами массивы, как указатель на char.
    void DeleteWords(char *s, char *tmp_word, int size_word)


    Так будет понятнее и проще. А еще лучше, передавайте std::string или std::vector. По ссылке, чтобы избежать копирования. По const ссылке, если не хотите, чтобы их внутри функции меняли.
    Ответ написан
    1 комментарий
  • Как найти число через проценты, по заданным условиям?

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

    В примере:

    60/100 = 3/5
    30/100 = 3/10
    10/100 = 1/10

    Знаменатели 5,10 и 10. НОК(5, 10, 10) = 10.

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

    Пусть n2 равно 1, если есть число в процентах, не делящееся на 2, и равно 0 - иначе.
    n4 - тоже для 4, n5, n25 - тоже для 5 и 25.

    Тогда ответ 2^(n2+n4)*5^(n5+n25).

    В примере есть число не делящееся на 4 (10 и 30), поэтому n4=1. Все четные - поэтому n2=0.
    n5 = 0, потому что все делится на 5. n25=1 - все не делится на 25. Поэтому ответ 2^(0+1)*5^(0+1) = 2*5=10

    edit:

    Вы обновили вопрос - проценты у вас могут быть нецелые. Например 3.71%

    Тогда забейте на 2 и 5 - проще через НОК считать. Опять же сократите дроби и возьмите знаменатели:
    3.71% = 371/10000
    3.72% = 372/10000 = 186/5000 = 93/2500
    и т.д.
    Ответ написан
  • Как разбить множество на всевозможные два подмножества с равной суммой элементов?

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

    Можно итеративно - перебирайте число от 0 до 2^|количество чисел|. Биты в этом числе будут обозначать, берется ли конкретный элемент множества в сумму. Дальше считайте сумму текущего подмножества пользуясь бытовыми операциями, чтобы проверить стоит ли заданный бит в 0 или 1. Если набрали половину общей суммы - выводите те индексы, где биты 1 как одно множество, а оставшиеся - как второе. Чтобы каждое разбиение не выводить 2 раза, требуйте, чтобы старший бит был 0 (перебирайте до 2^|количество чисел-1|)

    Можно рекурсивно. Рекурсивная функция принимает текущий набор чисел и номер следующего числа кандидата. Функция или берет или не берет текущего кандидата и запускается рекурсивно передавая следющий номер как кандидата. Если все числа рассмотрены - проверяйте сумму и выводите разбиение. Можно соптимизировать - если уже набрали сумму больше половины, дальше перебирать смысла нет. Опять же, первое число можно потребовать всегда брать или всегда не брать, чтобы не выводить одно и то же разбиение 2 раза.
    Ответ написан
    Комментировать
  • Можно ли восстановить дерево хаффмана с помощью стэка?

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

    Можно вместо рекурсии использовать стек вручную.

    Edit: а вручную будет примерно так: Читаем бит. Если 0, то кладем в стек 0. Если 1, то текущий стек - код симола по следующим 8 битам и удаляем из стека все 1 сверху до первого нуля. Нуль меняем на 1.
    Правда это даст не дерево а только коды символов. Чтобы получить дерево надо будет в стеке хранить указатели на вершины.
    Ответ написан
  • Как найти все завершённые пути на карте?

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

    С обработкой контура с краем чуть-чуть сложнее. Тут можно добавить в граф фиктивную вершину, представляющую собой все края и соединить ее с фигурами, соединенными с краем. Дальше тем же самым алгоритмом надо проверить на наличие циклов. Есть только одно исключение - фигура в углу может сама иметь 2 ребра в край. Но тут можно добавить проверку в сам алгоритм - надо не останавливаться, если глубина стека равна 2, т.е. цикл получается только из края и одной фигуры.
    Ответ написан
    Комментировать
  • Как найти следующий корень уравнения, используя метод простых итераций?

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

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

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

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

    Когда подберете параметры, функция будет вычислятся кусочно. Сначала найдите к какому отрезку относится текущее время (или тупо циклом или набором if-else, или, если сделаете отрезки одинаковым количеством часов, то можно поделить время на длину отрезка и округлить вниз, чтобы получить номер отрезка). Потом вычислите значение нужного сплайна, что будет просто вычислением полинома второй или третьей степени.

    Ну, или можете просто задать нексолько ключевых значений, и проинтерполировать полиномом лагранжа. Правда тут сложно будет заставить его идти именно как вам хочется. Через точки-то он точно пройдет, но вот между ними может иметь какие-то левые пики и изгибы. Так что придется поэксперементировать. Можно поиграться, например, в wolframalpha.com (введите "interpolating polynomial calculator", потом задайте значения функции в точках и получите и график и формулу. Ссылку дать не могу, qna ссылки на wolfram банит за одно со спамерскими ссылками по ошибке).
    Ответ написан
    Комментировать
  • Почему base64 увеличивает длину строки?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Почему при переходе из 16-значной системы в 64-значною длинна строки увеличилась, а не уменьшилась?


    Потому что на самом деле у вас переход из 256-ричной системы в 64-ричную. Base64 позволяет кодировать любые строки, а не только из 16 символов 0-9,a-f.

    Если вы хотите вашу строку интерпретировать как 16 запись, то вам надо ее сначала из hex записи раскодировать. Гуглите HexToBase64, hexToAscii или HexDecode. Какая нибудь такая функция наверняка есть в библиотеке. Или ее можно самостоятельно написать, превращая по 2 символа в 1.

    Да, только это все возможно только если у вас длина исходной строки четная.
    Ответ написан
  • Почему в моем коде портится содержимое переменных?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Проблема в переменной file_name, а в не функции. Судя по адресу, она на стеке. Причем на месте где-то в середине лакальной переменной buffer.

    Это значит, что вы из какой-то функции раньше вернули указатель на локальную переменную и получили висячий указатель.
    Ответ написан
    1 комментарий
  • Как построить блок схему?

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

    Может вместо конструкций C++ типа cin, cout, printf надо использовать псевдокод. Типа "ввод x"
    Ответ написан
    5 комментариев
  • Как решить олимпиадную задачу с графами?

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

    Вам надо только понять какие вершины в цикле, а какие на черенке. Тут можно обходом в глубину или ширину это найти. Или даже тупо циклом. В графе есть одна вершина степени 1 - она же выезд. От нее надо идти к соседним, пока не дойдете до единственной вершины степени 3. Пройденные так вершины будут черенком. Остальные - в цикле.

    А дальше вам надо посчитать длины трех маршрутов и выбрать из них лучший.
    Ответ написан
    Комментировать
  • Как реализовать алгоритм экспайринга элементов в базе данных?

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

    По нормальному, это должен быть один поток с приоритетной очередью, который работает постоянно.

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

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

    Обычно такое реализуется через какой-то примитив событий: не знаю, что там есть в go. Должна быть функция ожидания события с таймаутом. Вот там надо ждать события, которое выстреливает при добавлении новой таски пользователю, а таймаут должен браться из приоритетной очереди.

    При падении этого потока его можно перезапустить. При старте он должен получить из базы данных пока не истекшие таски и сложить их все в очередь и сразу удалить все с истекшем сроком.
    Ответ написан
    Комментировать
  • Как определить количество разных букв алфавита?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Попробуйте константу ascii уведичить до 256. Русские буквы в cp1251 идут во второй половине алфавита.

    Edit, ну и тип строки unsigned char надо. А то к отрицательным индексам будет доступ.
    Ответ написан
  • Как специализировать метод родительского класса?

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

    Определите shader::construct и пометьте override.
    Ответ написан
  • Как использовать переменную из одной функции в другой, не запуская при этом работу второй функции?

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

    Вы там выделяете новую память и заполняете ее, но снаружи это нигде не видно.

    Или можно наоборот - выделять массив в info, но тогда надо не выделять его в detal и передавать указатель на указатель, что бы info могла изменять stu. Или лучше, пусть info массив возвращает.
    Ответ написан
    Комментировать
  • Как найти сумму N векторов, взятых по одному из N множеств векторов, ближайшую к заданному вектору?

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

    Поэтому симплекc метод тут никак не поможет. Это задача квадратичного программирования, в лучшем случае.

    Тут или полный перебор, или какой-нибудь метод отжига придется делать. В одномерном случае при небольших числах можно какое-нибудь динамическое программирование использовать, но это вряд ли ваш случай.
    Ответ написан
    Комментировать
  • Как удалить элементы векторов по одинаковому индексу С++?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    int index = std::distance(NUMBER.begin(), it);
    TYPE.erase(TYPE.begin() + index);
    PRICE.erase(PRICE.begin() + index);
    ...


    Вы же удаляете элемент по индексу равному значению удаляемого элемента.

    Я бы, правда, вместо кучи массивов завел один массив со структурами. Это сильно упростит код, хоть и может в некоторых крайних случаях работать чуть медленее.
    Ответ написан
    1 комментарий
  • Как объединить/увидеть пересечение множеств через цикл?

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

    Ну так для объединения надо вывести все множество B и только ту часть A, которая не в пересечении.
    Ответ написан