• В чём ошибка вычисления бесконечно убывающей прогрессии с точностью до эпсилон?

    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 секцию исполняемого файла.
    Ответ написан
    Комментировать
  • C выдаёт ошибку при попытке сравнить 2 int?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Отключити оптимизацию и соберите проект в режиме Debug.

    У вас там Undefined Behavior и, очевидно, проблемы работы с памятью. Ошибку вы замечаете не там, где она, собственно, происходит.

    Сразу вижу проблему вот тут:
    strcat(result, alph[mod]);

    Тут вы приписываете строку с адресом alph[mod] к строке по адресу result. Обратите внимание, не строку, начинающуюся с позиции mod в массиве alph, а строку с адресом вроде кода символа 'F'.

    Ну и, вряд ли вы хотите часть строки alph скопировать в result. Плюс у вас в alph[] нет места под терминирущий 0, поэтому strcat вызовет переполнение буфера. Вам там надо дописывать один символ в конец result. Библиотечных функций именно для этого нет. Заведите счетчик количества символов в ответе и тупо переписывайте значение result[cnt].
    Ответ написан
    2 комментария
  • Тонкости Компиляторов. Почему в классах с++ не требуется объявление функции до вызова?

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

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

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

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

    Так, если вершина "+", то скобки ставить вообще не надо. Если вершина "*", то надо ставить скобки вокруг ребенка, если там операция "+" или "-". Если вершина "-", то надо ставить скобки справа, если там "+" или "-". Если вершина "/", то надо ставить скобки вокруг левого сына, если там "+" или "-". Вокруг правого сына скобки надо ставить если не число. Подумайте аккуратно, может я какие-то правила упустил.

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

    Парсинг - очень заезженная тема. Гуглите. Самый простой алгоритм - квадртатичный - проходитесь по строке и ищите самую левую операцию с самым большим приоритетом вне скобок (ведя счетчик открытых скобок). Если такой нет, то можно опустить скобки по краям. Если их нет, то это число (или перменная). Если нашли операцию, то рекурсивно разбирайте две подстроки слева и справа.
    Ответ написан
    5 комментариев
  • Почему в С++ не работают 2 цикла for?

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Обе книги классные. Мне Кормен показался попроще и по-понятнее. Кнут покрывает больше тем. Я бы начал с Кормена.
    Ответ написан
    1 комментарий
  • Как нарисовать кривую Серпинского (см. ниже), не используя графические библиотеки, а '*' или слешы?

    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 комментариев
  • Как решить олимпиадную задачу о трапеции?

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

    Совет: обозначте за x длину диагонали от точки пересечения к углу короткого основания. За y обозначьте оставшейся кусок диагонали (к большему основанию). У вас там 4 прямоугольных треугольнка образуются со сторнами xx, xy, yx, yy. Сумма их площадей - 110. Диагональ треугольника xx тоже дана (это короткое основание - 4). Искомое оснавание - диагональ треугольника yy (по теореме пифагора получается sqrt(2)*y).

    Составьте 2 уравнения, решите их, формулу запрограммируйте.
    Ответ написан
    Комментировать
  • Почему после ассемблера учить Си легче?

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

    Это понимание особенно важно тем, кто работает на относительно низкоуровневых языках (коим Си и является). Потому что абстракции не слишком далекие от ассемблера.

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

    Еще один фактор: Учить язык более высокого уровня всегда легче после освоения языка более низкого уровня. Ну как после подготовки к марафону вам будет сильно проще ходить на 3-ий этаж пешком. Потому что там за счет абстрагирования и отдаления от предыдущего уровня, становится легче писать программы. Поэтому после ассемблера будет легче учить и питон и си++ и хаскель. А после си будет проще учить си++, чем если бы вы сразу взялись за си++. При этом учтите, что общее потраченное время и силы на изучение ассемблера + си может быть больше чем просто обучение си.

    Но вообще, я бы советовал не учить сначала ассемблер. а потом си. Ибо досконально знать ассемблер вам не надо будет. Да и кучу времени потратите на него. Стоит учить их параллельно, не особо углубляясь в ассембрер. Сможете функцию вызвать, вывести на экран строку, сложить 2 введенных с экрана числа - уже хорошо.
    Ответ написан
    Комментировать
  • Не работает 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 в сложности. Перебирать углы становится еще хуже, но все еще возмоно.
    Ответ написан
    Комментировать
  • Какая space complexity у данного алгоритма?

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

    У этого алгоритма действительно пространственная сложность O(N) (если N - размер входного массива). Однако, можно натянуть сову на глобус, если отдельно считать входные и выходные данные (которых O(N)). Тогда можно сказать, что алгоритм использует O(1) дополнительно пространства. Входные и выходные данные все равно понадобятся, поэтому иногда их не считают.

    У меня нет доступа к этому видео. Если они там говорят "additional space is O(1)", то это именно так, как я описал выше.
    Ответ написан
    1 комментарий
  • Задания с CodeForces. Вариант решения есть, но не подходит для всех тестов. Может неправильно понял реализацию решения?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Для -3 ваша программа выведет 1, когда как правильный ответ 4 (+3-2-2-2).
    Ответ написан
    Комментировать
  • Как подступиться? Нужна ли равномерная сетка ВЫЧМАТ?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Да, можно воспользоваться этой оценкой. Что там за Mn я, правда, не знаю. Эту часть надо вычислять имея явный вид функции f(x)=e^x.
    Ответ написан
    Комментировать
  • Как сделать, что бы скрипт искал только фиксированное количество слагаемых?

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

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

    Более хитрые и быстрые варианты: динамическое программирование типа задачи о рюкзаке, где вы считаете количество способов набрать такую-то сумму стольки-то слагаемыми из стольки-то первых чисел. А потом используя это в рекурсивном переборе можно отрубать его тупиковые ветви: если ДП говорит, что там 0 наборов, то можно туда не идти в рекурсии. Или, если вам не сами наборы, а из количество нужно, то даже рекурсивного перебора не надо. Но это работает, только если target не очень большое.

    Второй более быстрый вариант: получить хоть бы и вашим перебором все варианты собрать все суммы среди первых n/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.
    Ответ написан
    Комментировать