pitchcontrol
10/27/2017 - 5:07 PM

Динамические алгоритмы

using System;
using System.Text;

namespace Dynamic.Algoriths
{
    class Program
    {
        /// <summary>
        /// На вершине лесенки, содержащей N ступенек, находится мячик, который начинает прыгать по ним вниз, к основанию. 
        /// Мячик может прыгнуть на следующую ступеньку, на ступеньку через одну или через 2. (То есть, если мячик лежит на 8-ой ступеньке, то он может переместиться на 5-ую, 6-ую или 7-ую.) 
        /// Определить число всевозможных «маршрутов» мячика с вершины на землю.
        /// </summary>
        /// <param name="n"></param>
        static void BallJump(int n)
        {
            //На первую ступеньку можно попасть только одним образом — сделав прыжок с длиной равной единице. 
            //На вторую ступеньку можно попасть сделав прыжок длиной 2, или с первой ступеньки — всего 2 варианта.
            //На третью ступеньку можно попасть сделав прыжок длиной три, с первой или со втрой ступенек. 
            //Т.е.всего 4 варианта(0->3; 0->1->3; 0->2->3; 0->1->2->3). 
            //Теперь рассмотрим четвёртую ступеньку. На неё можно попасть с первой ступеньки — по одному маршруту на каждый маршрут до неё, со второй или с третьей — аналогично.
            //Иными словами, количество путей до 4 - й ступеньки есть сумма маршрутов до 1 - й, 2 - й и 3 - й ступенек.Математически выражаясь, F(N) = F(N - 1) + F(N - 2) + F(N - 3).
            //Первые три ступеньки будем считать начальными состояниями.
            int[] vars = new int[3];
            vars[0] = 1; vars[1] = 2; vars[2] = 4;

            for (int i = 3; i < n; i++)
            {
                vars[i % 3] = vars[0] + vars[1] + vars[2];
            }
            //Так как каждое следующее значение зависит только от трёх предыдущих, ни одно значение под индексом меньше i-3 нам бы не пригодилось. 
            //В приведённом выше коде мы записываем новое значение на место самого старого, не нужного больше. 
            //Цикличность остатка от деления на 3 помогает нам избежать кучи условных операторов.
            //Просто, компактно, элегантно.

            Console.WriteLine(vars[(n - 1) % 3]);
        }
        /// <summary>
        /// В прямоугольной таблице NxM в начале игрок находится в левой верхней клетке. 
        /// За один ход ему разрешается перемещаться в соседнюю клетку либо вправо, либо вниз (влево и вверх перемещаться запрещено). 
        /// Посчитайте, сколько есть способов у игрока попасть в правую нижнюю клетку.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        static int MatrixPath1(int i, int j)
        {
            if (i == 1 || j == 1) return 1;

            return MatrixPath1(i - 1, j) + MatrixPath1(i, j - 1);
        }
        static int MatrixPath2(int Imax, int Jmax)
        {
            var dp = new int[Imax, Jmax];

            for (int i = 0; i < Imax; i++)
            {
                for (int j = 0; j < Jmax; j++)
                {
                    if (i == 0 || j == 0)
                    {
                        dp[i, j] = 1;
                    }
                    else
                    {
                        dp[i, j] = dp[i - 1, j] + dp[i, j - 1];
                    }
                }
            }
            return dp[Imax - 1, Jmax - 1];
        }
        /// <summary>
        /// Имеется калькулятор, который выполняет три операции:
        /// Прибавить к числу X единицу;
        /// Умножить число X на 2;
        /// Умножить число X на 3.
        /// Определите, какое наименьшее число операций необходимо для того, чтобы получить из числа 1 заданное число N.
        /// Выведите это число, и, на следующей строке, набор исполненных операций вида «111231».
        /// </summary>
        /// <param name="n"></param>
        static void Calculator(int N)
        {
            //Правильное решение заключается в нахождении для каждого числа от 2 до N минимального количества действий на основе предыдущих элементов, 
            //иначе говоря: F(N) = min(F(N-1), F(N/2), F(N/3)) + 1. Следует помнить, что все индексы должны быть целыми.
            //Для воссоздания списка действий необходимо идти в обратном направлении и искать такой индекс i, что F(i)=F(N), 
            //где N — номер рассматриваемого элемента. Если i=N-1, записываем в начало строки 1, если i=N/2 — двойку, иначе — тройку.
            int[] a = new int[N + 1];
            a[1] = 0;
            int min;
            int i;
            for (i = 2; i < N + 1; i++)
            {
                min = a[i - 1] + 1;
                if (i % 2 == 0) min = Math.Min(min, a[i / 2] + 1);
                if (i % 3 == 0) min = Math.Min(min, a[i / 3] + 1);

                a[i] = min;
            }
            StringBuilder ret = new StringBuilder();
            i = N;
            while (i > 1)
            {
                if (a[i] == a[i - 1] + 1)
                {
                    ret.Insert(0, 1);
                    i--;
                    continue;
                }

                if (i % 2 == 0 && a[i] == a[i / 2] + 1)
                {
                    ret.Insert(0, 2);
                    i /= 2;
                    continue;
                }

                ret.Insert(0, 3);
                i /= 3;
            }
            Console.WriteLine(a[N]);
            Console.WriteLine(ret);
        }

        static void Main(string[] args)
        {
            BallJump(15);
            Console.WriteLine(MatrixPath1(8, 5));
            Console.WriteLine(MatrixPath2(8, 5));
            Calculator(123);
            Console.WriteLine("Hello World!");
        }
    }
}