Редакция для Обратный отсчёт терминала
Submitting an official solution before solving the problem yourself is a bannable offence.
Автор:
Разбор задачи «Обратный отсчёт терминала»
Идея задачи
Нам дано число n и количество операций k.
За одну операцию происходит одно из двух действий:
- если последняя цифра числа не равна
0, число уменьшается на1; - если последняя цифра равна
0, эта цифра удаляется, то есть число делится на10.
Нужно узнать, какое число получится после ровно k таких операций.
Это задача на прямое моделирование процесса. Здесь не нужно искать сложную формулу или придумывать хитрую оптимизацию: достаточно аккуратно выполнить описанные в условии действия ровно k раз.
Как мыслить при решении
В задаче прямо сказано, что нужно делать на каждом шаге.
Значит, естественное решение такое:
- Повторить цикл
kраз. - Каждый раз смотреть на последнюю цифру числа.
- Если последняя цифра
0, делить число на10. - Иначе уменьшать число на
1.
Последнюю цифру числа удобно получать с помощью остатка от деления на 10:
n % 10— последняя цифра;n / 10илиn // 10— удаление последней цифры.
Почему это работает
Потому что мы в точности повторяем процесс из условия.
На каждом шаге возможны только два случая:
- последняя цифра не ноль — тогда нужно сделать
n = n - 1; - последняя цифра ноль — тогда нужно сделать
n = n / 10.
Если выполнить это ровно k раз, то мы получим ровно тот результат, который и требуется по условию.
То есть корректность решения следует напрямую из того, что алгоритм полностью совпадает с описанием операции.
Разберём пример
Пусть:
n = 512, k = 4
Тогда:
512— последняя цифра2, не ноль, получаем511511— последняя цифра1, не ноль, получаем510510— последняя цифра0, удаляем её, получаем5151— последняя цифра1, не ноль, получаем50
Ответ: 50.
Оценка сложности
Мы выполняем ровно k операций.
Каждая операция занимает константное время, потому что мы делаем только:
- проверку
n % 10, - либо
n -= 1, - либо деление на
10.
Итог:
- Время:
O(k) - Память:
O(1)
Это очень эффективно.
Эталонное решение на C++
#include <iostream>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
for (int i = 0; i < k; i++) {
if (n % 10 == 0) {
n /= 10;
} else {
n--;
}
}
cout << n << '\n';
return 0;
}
Пояснение к коду на C++
- считываем
nиk; - запускаем цикл на
kшагов; - если последняя цифра равна нулю, удаляем её делением на
10; - иначе уменьшаем число на
1; - после цикла выводим результат.
Эталонное решение на Python
n, k = map(int, input().split())
for _ in range(k):
if n % 10 == 0:
n //= 10
else:
n -= 1
print(n)
Пояснение к коду на Python
Логика полностью такая же:
n % 10даёт последнюю цифру;n //= 10удаляет последнюю цифру;n -= 1уменьшает число на единицу.
Типичные ошибки
1. Делить на 10 всегда, когда число оканчивается на 0, но забыть про остальные случаи
Некоторые участники обрабатывают только случай с нулём на конце и забывают, что иначе нужно просто вычесть 1.
2. Выполнить не k операций, а меньше
Например, остановиться, когда число стало маленьким. По условию нужно сделать ровно k операций.
3. Использовать вещественное деление
В Python нужно использовать именно //, а не /, потому что / делает вещественное деление.
Правильно:
n //= 10
Неправильно:
n /= 10
4. Пытаться придумать слишком сложное решение
Задача очень простая по идее. Здесь не нужна математика, двоичный поиск, динамика или сложные структуры данных. Достаточно честно промоделировать процесс.
Комментарии