Ответы пользователя по тегу C++
  • Как правильно подключить #include?

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

    Надо forward declaration расставить кое где. Чтобы не было зависимости от порядка include в файлах, стоит, наверно, расставить их в обоих файлах:
    class c_image;
    class c_window {
    // ...
    }


    И для красоты стоит image.hpp включать не в window.hpp, а window.cc. Ну и для второго класса аналогично сделать.

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

    Edit: Да, ошибка возникает потому, что в каком-то cc файле сначала вставился image.hpp, из которого вставился window.hpp (внутренний image.hpp в нем не вставляется из-за pragma once. Иначе бы была бесконечная рекурсия). В итоге у вас в .cc файле сначала идет определение класса window, и только потом класса image.
    Ответ написан
    Комментировать
  • Какая обёртка позволяет разыменовывать без неопределённого поведения?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ну просто проверяйте в operator*: если nullptr, то бросайте исключение.
    Ответ написан
    2 комментария
  • Издержки полиморфизма или неправильный дизайн?

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

    В хорошем дизайне у вас какая-нибудь функция print будет. Которая будет соответствующее число красиво, в соответствии с типом, выводить. Или рисовать на экране что-то, или считать что-то.

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Целочисленное переполнение при умножении i на i+1. Замените на, скажем prog += 1/((double)i*(i+1));, должно сработать.

    При i порядка 50000 результат перемножения не влезает в int. А у вас там 6697830 операций. В результате используются отрицательные или слишком маленькие неправильные значения i для вычисления слагаемых после 50000, и результат вообще не правильный.

    Ну и, кстати, логика решения у вас неправильная. Надо не с конечным значением сравнивать, а останавливаться, когда следующее слагаемое становится слишком маленьким.
    Ответ написан
    5 комментариев
  • Откуда взялся const?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    const char* взялся вот отсюда: "Hello world". Это строковая константа в коде. Ее программа менять никак не может. Компилятор ее засовывает в read only секцию исполняемого файла.
    Ответ написан
    Комментировать
  • Тонкости Компиляторов. Почему в классах с++ не требуется объявление функции до вызова?

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

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    for(; i < n; i++) Ничего глаз не режет? Зачем там точка с запятой после скобочки?
    Ответ написан
    Комментировать
  • Как нарисовать кривую Серпинского (см. ниже), не используя графические библиотеки, а '*' или слешы?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Напрягает требование, что нельзя использовать контейнеры. Так-то ваш подход правильный: Заводим поле для вывода, рекурсивной функцией, которой передаются порядок кривой, и где ее рисовать (квадрат и его поворот). Функция рекурсивно вызывает 4 кусокчка в каждом из 4 квадратов и рисует 3 соединительных кусочка. Но поле для вывода это так или иначе массив. Можно и самому его завести, но почему нульзя использовать контейнеры - не понятно.

    Второй вариант: релизовать функцию, которая (опять рекурсивно) считает какой символ вот на этой позиции в кривой вот такого порядка. Функция проверяет, лежит ли искомый символ между четырьмя квадратами. Если да, то сразу понятно - стоит там пустота или один из трех соединительных кусочков. В противном случае, рекурсивно вызываемся для нужного квадрата, пересчитав координаты, и может быть поворачиваем ответ на нужный угол.

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

    edit: Да, еще есть трюк - считайте, что кривая не замкнута. Левый верхний угол - пустота. И надо отдельно в самом конце замкнуть ее в этом углу через "/".
    Ответ написан
    6 комментариев
  • Как из первых 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 сам эту оптимизацию уже не применяет? Зачем вы какие-то свои велосипеды строите?
    Ответ написан