Подтема 4 из 5
Решение перебором
Программа перебором для задачи темы: что ищем, где остановиться и какие границы взять.
Шаблон программы
Вернёмся к задаче темы: чётному N справа дописывается 10, нечётному — 01, нужно наименьшее N, для которого R > 150.
Программа перебирает N по порядку, для каждого буквально выполняет алгоритм и проверяет результат. Сначала убедимся, что алгоритм записан правильно: напечатаем результаты для маленьких N и сверим с примером из условия или с ручным счётом.
for n in range(1, 9): s = bin(n)[2:] if n % 2 == 0: s = s + '10' else: s = s + '01' r = int(s, 2) print(n, r)Проверка руками: 5 = 101, нечётное, дописываем 01 — 10101 = 21. Совпало. Теперь фильтруем по вопросу: печатаем первое N, для которого R > 150, и останавливаем цикл.
for n in range(1, 200): s = bin(n)[2:] if n % 2 == 0: s = s + '10' else: s = s + '01' r = int(s, 2) if r > 150: print(n, r) breakОтвет — 38. Первое найденное N действительно наименьшее: все меньшие уже проверены. Дальше разберём, когда так остановиться нельзя и как выбрать границу перебора.
Вопрос поиска
Сначала пойми, что именно ищут: наименьшее N, наименьшее R или что-то ещё. В задании могут попросить разные вещи. Проверка во всех случаях — буквальный перевод условия, а выбирают и выводят разное. Выбери формулировку:
- наименьшее N, для которого R > T
- проверяем R > Tвыбираем минимальное N
- наибольшее N, для которого R ≤ T
- проверяем R ≤ Tвыбираем максимальное N
- наименьшее возможное R > T
- проверяем R > Tвыбираем минимальное R
- наибольшее возможное R ≤ T
- проверяем R ≤ Tвыбираем максимальное R
- N, при котором R минимально среди R > T
- проверяем R > Tсначала минимизируем R, затем выводим соответствующее N
Последний вариант особенно важен. Рассмотрим учебный алгоритм: если N чётное, справа дописать 111; если нечётное — дописать 0. Чётные N дают большие результаты, нечётные — маленькие.
Пусть требуется R > 8. Тогда минимальное подходящее N равно 2, поскольку оно даёт R = 23; минимальное подходящее R равно 10, оно получается при N = 5.
Почему минимальный результат именно 10? Число 9 получить нельзя: нечётные N дают чётные результаты, а чётные N ≥ 2 дают результаты не меньше 23.
Это пример того, что при увеличении N результат может уменьшаться.
Поэтому для наименьшего R программа не останавливается на первом подходящем N, а проверяет все входы и запоминает лучший результат и вход, на котором он получен. Запусти программу по шагам: best_r сначала станет 23 при N = 2, а потом уменьшится до 10 при N = 5.
best_n = 0best_r = 1000for n in range(2, 6): r = 2 * n if n % 2 == 0: r = 8 * n + 7 if r > 8 and r < best_r: best_n = n best_r = rprint(best_n, best_r)Начальное значение 1000 заведомо больше любого результата здесь, поэтому первый подходящий R его заменит. Перебор начат с 2, потому что N = 1 даёт R = 2 — оно не больше 8. Граница 6 в range пока взята для примера; как выбирать её честно, разберём ниже.
Остановка перебора
Остановить перебор на первом подходящем числе можно, только если понятно, почему оно лучшее. Команда завершает ближайший цикл. Если мы перебираем N = 1, 2, 3, … и ищем минимальное N, то первый подходящий вариант действительно минимален: все меньшие уже проверены. Зависимость R от N при этом может быть любой.
Но если ищем минимальное R, первый подходящий вход не обязательно даст лучший результат. Это показал предыдущий пример.
Остановиться на первом подходящем N и для наименьшего R можно, когда справа всегда дописывается одинаковое число цифр, а исходная запись сохраняется: тогда больший вход всегда даёт больший результат.
Тонкости:почему результат растёт
Есть важный случай, когда остановка допустима и для минимального результата. Пусть исходная запись N полностью сохраняется, справа всегда дописывается одно и то же количество k цифр, основание q фиксировано. Тогда R = N · qᵏ + A, где 0 ≤ A ≤ qᵏ − 1. Посмотрим, где лежат результаты соседних N при k = 2 в двоичной системе:
Для N результаты находятся в блоке 4N, 4N + 1, 4N + 2, 4N + 3. Для следующего числа начинается следующий блок: 4N + 4, … Они не перекрываются. Следовательно, при таком преобразовании результат строго растёт вместе с N — даже если дописываемые цифры выбираются по сложному условию.
Для данного N: N · qᵏ ≤ R ≤ N · qᵏ + qᵏ − 1.
А для следующего числа N + 1 результат не меньше (N + 1) · qᵏ = N · qᵏ + qᵏ. Это уже больше максимума для предыдущего N. Значит, любой результат входа N + 1 больше любого результата входа N.
Если длина дописываемого блока меняется или исходные цифры заменяются, это рассуждение уже неприменимо.
Границы перебора
Где остановить перебор, подсказывает сама задача, а не привычка писать range(1, 1000). Перебор — это проверка всех кандидатов подряд. Он даёт обоснованный ответ, если выполнены два условия:
- каждый кандидат обрабатывается точно по алгоритму;
- все кандидаты, способные повлиять на ответ, входят в диапазон.
Главный случай: алгоритм только дописывает цифры, сохраняя исходную запись. Тогда R ≥ N. Пусть ищем минимальное подходящее R и уже нашли какой-то результат R₀. Любой результат лучше него будет не больше R₀. Значит, для такого результата N ≤ R ≤ R₀. Поэтому все входы N > R₀ можно исключить. Это даёт строгую границу даже без знания окончательного ответа.
При удалении цифр, инверсии или замене начала записи неравенство R ≥ N может быть неверным. Его нельзя использовать автоматически.
Тонкости:граница через число дописанных цифр
Пусть справа всегда дописывают k цифр в системе с основанием q. Тогда R = N · qᵏ + A ≥ N · qᵏ. Если требуется R ≤ T, то обязательно N · qᵏ ≤ T. Следовательно:
N ≤ ⌊T / qᵏ⌋.
Например, справа дописывают две троичные цифры и требуют R ≤ 300: 9N ≤ 300, N ≤ 33. Больше 33 проверять не требуется.
Если количество дописываемых цифр меняется, но их всегда не меньше двух, то оценка R ≥ 9N по-прежнему верна.
Две проверки суммы цифр
Теперь полностью решим задачу с двумя последовательными проверками суммы цифр. Учебное условие:
- получить двоичную запись N;
- справа дописать остаток от деления суммы её цифр на 2;
- повторить действие для новой записи;
- найти минимальное N, для которого R > 100.
Хвост здесь всегда 00 или 10, то есть R = 4N или R = 4N + 2 (подтема 3). Следовательно, R ≤ 4N + 2. Если N ≤ 24, то R ≤ 4 · 24 + 2 = 98. Поэтому все N ≤ 24 точно не подходят.
Проверим 25: 25₁₀ = 11001₂. В записи три единицы — нечётное количество. Значит, дописывается 10: 11001 → 1100110, R = 4 · 25 + 2 = 102. Условие выполнено: 102 > 100. Следовательно, N_min = 25. Здесь мы не просто нашли подходящее число, но и доказали, что меньших нет.
Программа, буквально повторяющая условие:
def transform_binary(n): s = bin(n)[2:] remainder = s.count('1') % 2 s = s + str(remainder) remainder = s.count('1') % 2 s = s + str(remainder) return int(s, 2) for n in range(1, 27): r = transform_binary(n) if r > 100: print(n) breakrange(1, 27) задаёт числа от 1 до 26 включительно: правая граница в не входит. Почему диапазона достаточно? При N = 26: R ≥ 4 · 26 = 104 > 100. То есть хотя бы один подходящий вход гарантированно встретится не позднее 26.
Если бы вопрос был о минимальном R > 100, ответом было бы 102. Здесь это следует и из доказанного роста R вместе с N: справа всегда дописываются ровно две цифры.
Задан результат
Если результат задан, программа ищет вход, при котором он получается; разные результаты удобно собирать в множество. Обратный ход удобен руками, но программе проще пройти по входам и сравнить результат с заданным. Граница перебора берётся из того же рассуждения: алгоритм только дописывает цифры, значит R ≥ N, и входы больше R проверять не нужно.
Когда спрашивают количество различных результатов, одинаковые значения R, полученные от разных входов, учитывают один раз. Их удобно складывать в : оно хранит значения без повторов.
results = set()for n in range(1, 103): r = transform_binary(n) results.add(r) if r == 102: print(n) print(100 in results, len(results))Вход для 102 — это 25; число 100 не получается ни из одного входа. Различных результатов 102 — столько же, сколько входов: при дописывании ровно двух цифр разные N дают разные R. Для алгоритма, который отбрасывает цифры, это уже не так.
Троичный алгоритм
Разберём троичный алгоритм, в котором длина дописываемого блока меняется. Учебное условие:
- построить троичную запись N;
- если N делится на 3, дописать справа две последние цифры этой записи;
- иначе остаток от деления N на 3 умножить на 7, перевести произведение в троичную систему и дописать справа;
- найти максимальное R, не превышающее 300.
Разделим все входы по остатку от деления на 3. Если остаток равен 0, дописываются две последние цифры: R = 9N + (N mod 9). Если остаток равен 1: 1 · 7 = 7 = 21₃, дописали две цифры, R = 9N + 7. Если остаток равен 2: 2 · 7 = 14 = 112₃, дописали уже три цифры, R = 27N + 14.
Например, N = 2 даёт R = 68, а N = 3 — R = 30. Результат уменьшился при увеличении входа. Поэтому нельзя остановиться, впервые получив значение больше 300, и считать дальнейший поиск бесполезным.
Но можно строго ограничить входы. Во всех ветвях дописывается не меньше двух троичных цифр: R ≥ 9N. При R ≤ 300 получаем N ≤ 33.
Программа перебирает N от 1 до 33 и запоминает наибольший подходящий результат; она использует функцию to_base:
def transform_ternary(n): s = to_base(n, 3) if n % 3 == 0: s = s + s[-2:] else: extra = (n % 3) * 7 s = s + to_base(extra, 3) return int(s, 3) best_r = -1 for n in range(1, 34): r = transform_ternary(n) if r <= 300: if r > best_r: best_r = r print(best_r)best_r хранит лучший результат среди уже проверенных. Начальное значение -1 выбрано потому, что все возможные результаты неотрицательны. Поэтому первый подходящий результат окажется больше него. Мы сравниваем именно r, поскольку вопрос требует максимальное выходное число.
Тонкости:тот же ответ без программы
Найдём лучший вариант в каждой ветви и сравним их. Для остатка 0 кандидаты около верхней границы — 30 и 33: N = 33 даёт R = 9 · 33 + 6 = 303 — не подходит; N = 30 даёт R = 9 · 30 + 3 = 273. При меньших положительных кратных 3, то есть N ≤ 27, R ≤ 9 · 27 + 6 = 249. Значит, лучший результат этой ветви — 273.
Для остатка 1: 9N + 7 ≤ 300, N ≤ 32,55… Наибольшее целое с остатком 1 при делении на 3 — это 31: R = 9 · 31 + 7 = 286.
Для остатка 2: 27N + 14 ≤ 300, N ≤ 10,59… Наибольшее подходящее N с остатком 2 — это 8: R = 27 · 8 + 14 = 230.
Сравниваем: max(273, 286, 230) = 286. Ответ: 286.
Итого
- Шаблонцикл по N, алгоритм буквально, сверка с примером, фильтр по вопросу
- Наименьшее Nпервое подходящее при переборе по возрастанию
- Наименьшее Rперебрать всё и хранить лучший R и его N
- Границаалгоритм только дописывает цифры — R ≥ N
- Задан Rпроверка равенства; различные результаты — множество
Проверьте себя
Ответь сначала сам, потом открой ответ.
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
best_r и вход best_n, на котором он получен; в ответ идёт N.Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Наименьший R > 30 и его вход
Для алгоритма «чётному N дописать справа 111, нечётному — 0» найди наименьший результат R > 30 и вход, при котором он получается.
Введите два числа через пробел в том порядке, в каком их спрашивают.
Подсказка
Блоки разной длины: R не растёт вместе с N. Сохраняй R и N вместе.
Решение
R = 34 при N = 17: нечётные дают 2N, чётное N = 2 даёт только 23, а чётные N ≥ 4 — не меньше 8 · 4 + 7 = 39.
Решение программой
best_r = Nonebest_n = Nonefor n in range(1, 31): s = bin(n)[2:] s += '111' if n % 2 == 0 else '0' r = int(s, 2) if r > 30 and (best_r is None or r < best_r): best_r = r best_n = nprint(best_r, best_n)Ответ: 34 17
Минимальное N с R > 100
Чётному N справа дописывают 10, нечётному — 01. Найди минимальное N, для которого R > 100.
Введите число.
Подсказка
Справа всегда две цифры: R ≈ 4N.
Решение
N = 25: для нечётного 25 получается 4 · 25 + 1 = 101; для чётных нужно N ≥ 26.
Решение программой
n = 1while True: s = bin(n)[2:] s += '10' if n % 2 == 0 else '01' if int(s, 2) > 100: print(n) break n += 1Ответ: 25
Наибольшее R не больше 200
На вход алгоритма подаётся натуральное число N > 1. Строится двоичная запись N. Если N делится на 3, справа к ней дописываются две её последние цифры; иначе остаток от деления N на 3 умножается на 3, и двоичная запись произведения дописывается слева. Полученная запись — двоичная запись числа R. Укажи наибольшее R, не превышающее 200.
Введите число.
Подсказка
Дописывание не убирает цифр: R ≥ N, перебора до 200 хватает.
Решение
192 при N = 48: 110000₂ → 11000000₂. Дописывание не убирает цифр, поэтому R ≥ N и хватает N ≤ 200.
Решение программой
best = 0for n in range(2, 201): s = bin(n)[2:] if n % 3 == 0: s = s + s[-2:] else: s = bin(n % 3 * 3)[2:] + s r = int(s, 2) if r <= 200: best = max(best, r)print(best)Ответ: 192
Различные результаты переворота
Двоичная запись N переворачивается, ведущие нули отбрасываются, получившаяся запись — двоичная запись числа R. Сколько различных значений R получается при N от 1 до 100?
Введите число.
Подсказка
Различные результаты собирает множество.
Решение
У чётного N в конце записи ноль, после переворота он становится ведущим и отбрасывается — 2N даёт тот же R, что и N.
Решение программой
results = set()for n in range(1, 101): results.add(int(bin(n)[2:][::-1], 2))print(len(results))Ответ: 50
Троичный алгоритм для 14
Для N = 14 примени троичный алгоритм: при кратности 3 скопировать последние две цифры, иначе дописать троичную запись числа 7 · (N mod 3). Найди R.
Введите число.
Подсказка
14 не делится на 3: дописывается троичная запись 7 · 2.
Решение
14 = 112₃, дописываем 112; 14 · 27 + 14 = 392.
Решение программой
def to_base(n, q): if n == 0: return '0' s = '' x = n while x > 0: digit = x % q s = str(digit) + s x = x // q return s n = 14s = to_base(n, 3)if n % 3 == 0: s += s[-2:]else: s += to_base(7 * (n % 3), 3)print(int(s, 3))Ответ: 392
Вход для наименьшего R > 1000
На вход алгоритма подаётся натуральное число N. Строится троичная запись N. Если сумма её цифр чётная, справа дописывается 1; иначе слева дописывается 2. Полученная запись — троичная запись числа R. Укажи такое N, при котором R — наименьшее среди чисел, больших 1000.
Введите число.
Подсказка
В ответе — N, а сравнивать нужно R: храни оба числа.
Решение
334 = 110101₃, сумма цифр 4, R = 1101011₃ = 1003. R ≥ N, поэтому входы больше 1003 проверять не нужно.
Решение программой
def to_base(n, q): if n == 0: return '0' s = '' x = n while x > 0: digit = x % q s = str(digit) + s x = x // q return s best_r = Nonebest_n = Nonefor n in range(1, 1004): s = to_base(n, 3) total = 0 for c in s: total += int(c) if total % 2 == 0: s = s + '1' else: s = '2' + s r = int(s, 3) if r > 1000 and (best_r is None or r < best_r): best_r = r best_n = nprint(best_n)Ответ: 334