Разбор
Пусть содержит наименьшее количество операций, выполнение которых преобразует число в . Например,
, так как мы уже имем число ;
, преобразование ;
, преобразование ;
, преобразование ;
В случае выгоднее сначала вычесть , нежели воспользоваться идеей жадности и разделить на .
Рассмотрим процесс вычисления функции .
Если разделить число на (при условии что делится на ), то
Если разделить число на (при условии что делится на ), то
Если из числа вычесть , то
Из числа можно получить одно из трех чисел: или . Количество операций, за которое каждое из этих чисел можно свести к , равно и соответственно. Поскольку нас интересует наименьшее количество операций, то имеем следующее соотношение:
При этом если не делится на (или на ), то соответствующий элемент ( или ) отсутствует в функции . Например, для имеем:
Соответственно для получим:
Значения функции будем запоминать в ячейках массива , где . Заполняем ячейки массива от до согласно приведенному рекуррентному соотношению. Например, в следующей таблице приведены значения d[i] для :
Например,
То есть нам эффективнее вычитать из единицу, нежели делить ее на .
Упражнение
Вычислите значения для следующих значений :
Реализация алгоритма
Объявим массив , -ая ячейка которого содержит наименьшее количество операций, выполнение которых преобразует число в .
int d[1000001];
Заполняем ячейки массива согласно приведенным формулам.
d[1] = 0; for(i = 2; i <= 1000000; i++) { // d[i] = min(d[i/3],d[i/2],d[i-1]) + 1 d[i] = d[i-1]; if (i % 2 == 0 && d[i/2] < d[i]) d[i] = d[i/2]; if (i % 3 == 0 && d[i/3] < d[i]) d[i] = d[i/3]; d[i]++; }
Для каждого входного значения выводим ответ .
while(scanf("%d",&n) == 1) printf("%d\n",d[n]);
Реализация алгоритма — рекурсия
#include <stdio.h> #include <string.h> #define INF 2000000000 int i, res, n; int dp[1000001]; int min(int a, int b, int c) { int res = a; if (b < res) res = b; if (c < res) res = c; return res; } int f(int n) { if (n == 1) return 0; if (dp[n] != -1) return dp[n]; int a = f(n - 1); int b = (n % 2 == 0) ? f(n / 2) : INF; int c = (n % 3 == 0) ? f(n / 3) : INF; return dp[n] = min(a,b,c) + 1; } int main(void) { memset(dp, -1, sizeof(dp)); while (scanf("%d", &n) == 1) { res = f(n); printf("%d\n", res); } return 0; }
Java реализация
import java.util.*; public class Main { public static int MAX = 1000001; static int d[] = new int[MAX]; public static void main(String[] args) { Scanner con = new Scanner(System.in); d[1] = 0; for(int i = 2; i < MAX; i++) { d[i] = d[i-1] + 1; if (i % 2 == 0) d[i] = Math.min(d[i], d[i/2] + 1); if (i % 3 == 0) d[i] = Math.min(d[i], d[i/3] + 1); } while(con.hasNext()) { int n = con.nextInt(); System.out.println(d[n]); } } }
Python реализация
Создадим список , -ая ячейка которого содержит наименьшее количество операций, выполнение которых преобразует число в .
d = [0] * 1000001
Заполняем ячейки массива согласно приведенным формулам.
d[1] = 0 for i in range(2, 1000001): d[i] = d[i - 1] if i % 2 == 0 and d[i // 2] < d[i]: d[i] = d[i // 2] if i % 3 == 0 and d[i // 3] < d[i]: d[i] = d[i // 3] d[i] += 1
Для каждого входного значения выводим ответ .
while True: try: n = int(input()) print(d[n]) except EOFError: break