Ответы пользователя по тегу C++
  • Как из первых N натуральных чисел составить максимальное количество пар, суммы которых являются простыми?

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

    А главная проблема вашего решения в том, что оно "жадное". Вы жадно берете первую папавшуюся пару, как будто так будет оптимально. Но это не так. Например, если у вас остались гири весами 2, 3, 4, 5, то брать пару 2+3 = 5 нельзя, ведь тогда оставшиеся 4+5=9 дадут не простое число.

    Это действительно можно решать через задачу о паросочетании как сказал Alexandroppolus. Вот только все алгоритмы работают за O(N^3), так что для N=500000 это решение будет работь очень долго.

    Но в вашем случае граф очень специфичный, так что надо придумать какой-то шаблон. Например, можно взять максимальное простое число P <= N+1 и набирать его всеми возможными парами (1+(P-1), 2+(P-2)). В итоге у вас остнется одна нечетная гиря (P+1)/2 и все гири >=P. Например, если N+1 простое - то это оптимальный способ.

    Я бы посоветовал написать решение через максимальное паросочетание и прогнать его для всех N <=200. Получите оптимальные ответы, посмотрите на них. Поищите какой-то паттерн в количестве пар, потом попробуйте придумать способ такое количество набрать.

    И еще немного покритикую ваш код.
    f==true. Зачем? Можно написать if(f). А вообще вам тут f не нужно, пишите if(Check(i+j)).

    Вместо
    if(a[j]!=0&&a[i]!=0&&i!=j) { 
      ...
    }

    стоит использовать "ранний выход":
    if(!a[i] || !a[j] || i == j) continue; 
    ...


    Так вложенность и сложность кода меньше. Его проще читать и понимать.

    Вместо прверки, что i != j, можно внутренний цикл гнать от n до i+1. Вам же не надо перебирать отдельно пары 1+10 и 10+1? Всегда считайте, что первое число меньше второго в паре.

    Ну, и отступы, ради всего святого, расставьте аккуратно. Любой редактор кода умеет их делать автоматически.

    Edit:
    Совместо с Alexandroppolus в комментариях придумали следующее решение: Берете минимальное простое число, большее N. Обзовем его P. Тогда берем пары N+(P-N), (N-1)+(P-N+1)... и т.д. В этих парах одно число четное, другое нечетное. И всего они покроют отрезок четной длины без дырок. Потом задача сводится к такой же, только уже с максимальным числом не N, а P-N-1.
    Эта жадность работает, потому что она всегда соберет максимально теоретически возможное количетсво пар. Может только одна 1 в конце останется.
    Ответ написан
    6 комментариев
  • Не работает cin C++ KDevelop, как исправить?

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ну, у вас person.Age - это Property<int>. Как его в cout выводить вы нигде не определяли, про это компилятор и ругается. У Property вы, правда, определили приведение к int. так-что вот это сработает:
    cout << static_cast<int>(person.Age);

    Но сам компилятор догадаться, что это надо к инту приводить не может.

    Вам надо также, как вы operator<< для Person определили, определить его для Property<T>.
    Ответ написан
    3 комментария
  • Как найти минимальный ограничивающий параллелепипед?

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

    Кажется, есть эффективное решение через численную оптимизацию и троичный поиск.

    Введем функцию f(a,b) - минимальный объем параллелипипеда, повернутого на угол a вдоль оси oz и потом угол b вдоль ox. Ну или это эйлеровы углы, если хотите.

    Вам надо найти минимум этой функции. Утверждение: если зафиксировать a, то двигая b функция будет переодическая с двумя экстремумами. Ну, потому что поворот на 90 градусов взвращает все как было, только оси местами меняются. Значит, на ней можно что-то вроде тернарного поиска запускать (об этом дальше).

    Если же ввести функцию g(a) =Min_b(f(a,b)), то она, похоже будет такая же. По ней тоже можно такой же поиск запустить.

    Итого, 2 вложенных тернарных поиска, в внутри легче все точки повернуть на -b и -a градусов и потом взять обрамляющий параллелипипед, параллельный осям координат (min/max по трем координатам за 1 проход).

    В тернарном поиске делим отрезок всех углов на 3 части, считаем значение функции в двух промежуточных и крайних точках. Дальше придется повозиться со случаями, их много (12), Функция может сначала иметь максимум, потом минимум, или наоборот. И 6 вариантов, как 2 промежуточные точки лягут на 3 отрезка (возрастание,убываниние, возрастание или убывание, возрастание, убывание). Тут надо их все аккуратно нарисовать, подумать, какие соотношения четырех точек позволяют выкинуть один из трех отрезков между точками.

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

    Это будет что-то вроде O(n log^2 n).

    Или можно просто случайным образом или с малым шагом перебирать разные углы поворота. Поворачивать все точки и искать параллельный осям координат параллелипипед (просто беря min/max по трем координатам). Для 800 точек можно 10000 углов перебрать и это займет лишь 100мс на современном железе.

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

    Edit: вообще, кажется, там 3 угла, а не 2. Ну появляется еще третья функция t(a,b,c). и лишний Log n в сложности. Перебирать углы становится еще хуже, но все еще возмоно.
    Ответ написан
    Комментировать
  • Задания с CodeForces. Вариант решения есть, но не подходит для всех тестов. Может неправильно понял реализацию решения?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Для -3 ваша программа выведет 1, когда как правильный ответ 4 (+3-2-2-2).
    Ответ написан
    Комментировать
  • Как передавать Bitmap в функции? Как возвращать Bitmap?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Скорее всего не получается вернуть объект типа Bitmap, потому что его нельзя копировать. Попробуйте возвращать через std::move или возвращайте, например std::unique_ptr<gdiplus::Bitmap>.
    Ответ написан
    Комментировать
  • Как удалить Bitmap?

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

    hBitmap - да, удалять надо.
    В документации этот момент даже расписан:
    You are responsible for deleting the GDI bitmap and the GDI palette. However, you should not delete the GDI bitmap or the GDI palette until after the GDI+ Bitmap object is deleted or goes out of scope.


    Это делается через DeleteObject.
    Ответ написан
    Комментировать
  • Почему мой код не проходит по времени?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    У вас не самое эффективное решение. Что-то типа O(n sqrt(n)), когда как задачу можно решить за O(n).

    Сначала решетом эратосфена найдите для каждого числа минимальный простой делитель. У меня даже статья про этот алгоритм есть с кодом и объяснением.

    Потом, решение за O(n log n) - можно быстро найти все простые множители каждого числа используя массив с предущего шага. Считайте степени простых множителей и перемножайте их +1. Так 2^2*3^1 имеет (2+1)(1+1) = 3*2 = 6 делителей (включая 1 и 12. Если они не нужны, вычтите 1-2 в конце).

    Но можно сделать еще быстрее. Фактически применив динамическое программирование. Во втором массиве посчитайте степень минимального простого делителя для каждого числа. Если мнимальный делитель (p[i]) для i равен мнимальному для i/p[i], то степень для i будет 1 + степень для i/p[i]. Иначе она будет 1. Так же посчитайте для каждого числа что там останется, если выкинуть все вхождения минимального делителя. Потом в другом массиве посчитайте для каждого числа количество делителей - это ответ для числа без всех вхождений минимального, умноженный на количество этих минимальных делителей +1. Ну а потом по этому массиву считайте сумму. Это будет работать за O(n).
    Ответ написан
  • Как заполнить матрицу из массива?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Элемент в строке i в столбце j (считая с 0) в линейном массиве будет иметь номер i*n+j (или там на m надо вместо m умножать. Там должно быть количество столбцов).

    Соответственно, программа должна двумя вложенными циклами присваивать в ячейку [i][j] вот тот вот элемент из массива.
    Ответ написан
    3 комментария
  • Можно ли обратиться к полю дочернего класса через указатель на базовый?

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

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

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


    У вас 2 раза определен class парсер::ВыражениеParser.

    Компилятор даже указал на оба определения: они оба в файле ВыражениеParser.h, но включенного 2 раза из разных исходников:
    In file included from ВыражениеVisitor.h:8,
                     from ВыражениеParser.cpp:6:
    ВыражениеParser.h:15:8: ошибка: повторное определение «class парсер::ВыражениеParser»
    ...
    In file included from ВыражениеListener.h:8:
    ВыражениеParser.h:13:8: замечание: предыдущее определение «class парсер::ВыражениеParser»


    Пока похоже, что вы там намудрили с include-guard'ами из-за чего один и тот же хедер включается несколько раз. Выкладывайте начало файла ВыражениеParser.h.
    Ответ написан
  • Как вывести буквы, которые используется наиболее кол-во раз?

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

    Первая задача решается просто - заведите массив счетчиков. Например, на 256 элементов. Для каждой буквы строки увеличивайте счетчик с индексом равным символу (помните, же, что char в C++ - это целочисленный тип?)

    Ну, вторая задача - это элементарное упраждение. Например, заведите переменную, в которой будете хранить индекс максимума. Пройдитесь по массиву счетчиков (от 0 до 255), и изменяйте этот индекс, если текущее значение больше максимального.
    Ответ написан