Розбір
Let represent the minimum number of operations required to transform the number into . For example,
, as we already have the number ;
, perform operation ;
, perform operations ;
, perform operations ;
In the case of , it is more advantageous to subtract first rather than using the greedy approach and dividing by .
Let’s consider the process of computing the function .
If we divide the number by (assuming is divisible by ), then
If we divide the number by (assuming is divisible by ), then
If we subtract from the number , then
From the number , we can obtain one of three numbers: , or . The number of operations required to reduce each of these numbers to , is equal to , and respectively. Since we are interested in the minimum number of operations, we have the following relationship:
In the case, if is not divisible by (or by ), then the corresponding element ( or ) is absent in the function. For example, for , we have:
Similarly, for , we get:
The values of the function will be stored in the cells of the array , where . Fill the cells of the array from to according to the given recurrence relation. For example, the following table shows the values of for :
For example,
In other words, it is more efficient to subtract from , rather than divide it by .
Exercise
Find the values of for the following values of :
Algorithm realization
Declare an array , where the -th cell contains the minimum number of operations required to transform the number into .
int d[1000001];
Fill the cells of array according to the provided formulas.
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]++; }
For each input value , print the answer .
while(scanf("%d",&n) == 1) printf("%d\n",d[n]);
Algorithm realization — recursion
#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 realization
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 realization
Create a list , where the -th cell contains the minimum number of operations required to transform the number into .
d = [0] * 1000001
Fill the cells of array according to the provided formulas.
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
For each input value , print the answer .
while True: try: n = int(input()) print(d[n]) except EOFError: break