Задать вопрос
  • Как нарисовать кривую Серпинского (см. ниже), не используя графические библиотеки, а '*' или слешы?

    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.
    Ответ написан
    Комментировать
  • Как правильно умножать восьмичные числа с плавающей точкой?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    В столбик. Точно так же как обычные десятичные числа, только помните, что цифры 8 нет. Если получилось больше 7, все остальное, поделенное на 8 переносится дальше.
    Ответ написан
    Комментировать
  • Почему мой код не проходит по времени?

    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 комментария
  • Почему не отсеиваются нули при сравнении 0 < 0?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    array[index + 1] ? item < array[index + 1] : true Что эта конструкция делает, можете объяснить? Ошибка в ней.
    Ответ написан
    5 комментариев
  • Нужно ли писать суффиксы литералов?

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

    Вот когда вы константы передаете в какие-то перегруженные функции, где имеет значение unsigned ли ей передан или signed, или есть разница между float и double, то тогда - да, надо писать.
    Ответ написан
    4 комментария
  • Удалить из ряда элементы ,как?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Например, вот так:
    def GenerateAll(source, rep):
      for comb in itertools.combinations(range(len(source)), len(source)-len(rep)):
        s = list()
        prev = 0
        for (i, j) in enumerate(comb):
          s.append(source[j]);
        yield "".join(s)


    Обратите внимание, перебираем не сочетания из удаляемых позиций, а из 22 оставляемых (кстати, у вас числа не бьются, 48+22 = 60 != 64). Тут порядок в ответе будет обратный (первая строка будет "xxxxxx...xxxx12..." а не "12..xxx..xx". Если нужен тот же порядок, то будет чуть сложнее:
    def GenerateAll(source, rep):
      for comb in itertools.combinations(range(len(source)), len(rep)):
        s = list()
        prev = 0
        last = 0
        for (i, j) in enumerate(comb):
          while (last < j):
              s.append(source[last]);
              last += 1
          last += 1
        while (last < len(source)):
              s.append(source[last]);
              last += 1
        yield "".join(s)


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

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

    Можно было бы предположить, что оно повиснет, как при делении на 0 на механическом калькуляторе. Хоть это и прикольно выглядит.

    Но процессор не вычитает кучу раз подряд, ведь там двоичная система счисления. Разряд вычисляется проверкой на переполнение при одном вычитании со сдвигом, а результат идет дальше. Вот лекция о том, как устроена схема делителя.

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

    В итоге оно скорее всего выдаст неправильный результат. Что-то вроде 2^31-1 для любого делимого.

    Правда, если Intel/Amd/etc. нагородили каких-то оптимизаций или как-то усложнили схему, то результат может быть другим.
    Ответ написан
    Комментировать