Генерация 10-значного числа с помощью клавиатуры телефона

Учитывая клавиатуру телефона, как показано ниже:

1 2 3
4 5 6
7 8 9
  0

Сколько различных десятизначных чисел можно составить, начиная с 1? Ограничение состоит в том, что движение от одной цифры к другой похоже на движение коня в шахматной партии.

Например. если мы на 1, то следующая цифра может быть либо 6, либо 8, если мы на 6, то следующая цифра может быть 1, 7 или 0.

Допускается повторение цифр - 1616161616 является допустимым номером.

Существует ли алгоритм полиномиального времени, который решает эту проблему? Задача требует, чтобы мы просто дали количество 10-значных чисел и не обязательно перечисляли числа.

РЕДАКТИРОВАТЬ: я попытался смоделировать это как график, в котором каждая цифра имеет 2 или 3 цифры в качестве соседей. Затем я использовал DFS для перехода до глубины 10 узлов, а затем увеличивал количество чисел каждый раз, когда достигал глубины 10. Очевидно, что это не полиномиальное время. Если предположить, что у каждой цифры всего 2 соседа, это потребовало бы как минимум 2 ^ 10 итераций.

Здесь переменная — это количество цифр. Я взял напр. из 10-значных чисел. Это также может быть n-цифр.


person srikanta    schedule 23.05.2010    source источник
comment
Домашнее задание? что ты уже испробовал?   -  person Eric J.    schedule 24.05.2010
comment
@ Эрик Дж. Как это может быть не домашним заданием? ;)   -  person catchmeifyoutry    schedule 24.05.2010
comment
звучит как вопрос пролога.   -  person Randy    schedule 24.05.2010
comment
@srikfreak: Если на самом деле это домашнее задание, вы получите наилучшие результаты, если поставите ему этот тег и покажете, какие честные усилия вы приложили и где вы застряли. Большинство людей здесь не хотят делать вашу домашнюю работу за вас, и вы бы не научились, даже если бы кто-то это сделал.   -  person Eric J.    schedule 24.05.2010
comment
Об этом спросили в интервью. Я пометил это как вопрос интервью.   -  person srikanta    schedule 24.05.2010
comment
Полиномиальное время, выраженное через какую переменную? Без этого вопрос не имеет смысла. Как указано, его можно вычислить за постоянное время, поскольку есть только одна проблема (перечисление всех 10-значных чисел). Вы имеете в виду полиномиальное время с точки зрения количества цифр, на которых нужно остановиться? Полиномиальное время с точки зрения количества многозначных чисел?   -  person Doug McClean    schedule 24.05.2010
comment
Да, в самом деле. Здесь переменная — это количество цифр. Здесь n = 10 цифр. Обновлю вопрос, чтобы прояснить его.   -  person srikanta    schedule 24.05.2010
comment
5 никогда не может быть достигнуто, поэтому его можно безопасно игнорировать для этой конкретной проблемы, не оказывая никакого влияния на решение.   -  person srikanta    schedule 25.05.2010
comment
Для тех, кто хочет проверить свои решения, правильный ответ 1424   -  person Sarp Centel    schedule 07.03.2011


Ответы (11)


Конечно, это можно сделать за полиномиальное время. Это отличное упражнение по динамическому программированию или запоминание.

Предположим, что N (количество цифр) равно 10 для примера.

Подумайте об этом рекурсивно так: Сколько чисел я могу построить, используя 10 цифр, начиная с 1?

Ответ

[number of 9-digit numbers starting from 8] +
[number of 9-digit numbers starting from 6].

Итак, сколько существует "девятизначных чисел, начинающихся с 8"? Хорошо,

[number of 8-digit numbers starting from 1] +
[number of 8-digit numbers starting from 3]

и так далее. Базовый случай достигается, когда вы получаете вопрос "Сколько существует однозначных чисел, начиная с X" (и ответ, очевидно, 1).

Когда дело доходит до сложности, ключевое наблюдение заключается в том, что вы повторно используете ранее вычисленные решения. То есть, например, ответ на вопрос "сколько существует 5-значных чисел, начиная с 3", можно использовать как при ответе "сколько существует 6-значных чисел, начиная с 8 " И "сколько 6-значных чисел начинается с 4". Это повторное использование приводит к падению сложности от экспоненциальной до полиномиальной.

Давайте подробнее рассмотрим сложность решения динамического программирования:

Такая реализация заполнит матрицу следующим образом:

num[1][i] = 1, for all 0<=i<=9   -- there are one 1-digit number starting from X.

for digits = 2...N
    for from = 0...9
        num[digits][from] = num[digits-1][successor 1 of from] +
                            num[digits-1][successor 2 of from] +
                            ...
                            num[digits-1][successor K of from]

return num[N][1]                 -- number of N-digit numbers starting from 1.

Алгоритм просто заполняет матрицу одной ячейкой за раз, а матрица имеет размерность 10*N и, таким образом, выполняется за линейное время.


Написал с головы, поправьте, если есть опечатки.

person aioobe    schedule 23.05.2010
comment
Рабочее решение (с использованием Python3) с вашим алгоритмом на github. com/harishvc/challenges/blob/master/ . Не могли бы вы подробнее объяснить линейную временную сложность? - person harishvc; 20.03.2016
comment
@aioobe, поскольку мы вычисляем текущую строку в кеше на основе предыдущей, мы можем просто использовать int[10] и получить пространство O (1). - person Konstantin Milyutin; 31.01.2017

Я решил решить эту проблему и сделать ее максимально расширяемой. Это решение позволяет:

Определите свою собственную доску (телефонную панель, шахматную доску и т. д.)

Определите собственную шахматную фигуру (конь, ладья, слон и т. д.); вам нужно будет написать конкретный класс и сгенерировать его на заводе.

Получите несколько фрагментов информации с помощью некоторых полезных служебных методов.

Классы следующие:

PadNumber: класс, определяющий кнопку на панели телефона. Может быть переименован в «Квадрат», чтобы представить квадрат доски.

ChessPiece: абстрактный класс, определяющий поля для всех шахматных фигур.

Движение: Интерфейс, который определяет методы движения и позволяет фабрично генерировать детали.

PieceFactory: Фабричный класс для создания шахматных фигур.

Knight: конкретный класс, который наследуется от ChessPiece и реализует движение.

PhoneChess: Вступительный класс.

Водитель: Код водителя.

Хорошо, вот код :)

package PhoneChess;

import java.awt.Point;

public class PadNumber {

private String number = "";
private Point coordinates = null;

public PadNumber(String number, Point coordinates)
{
    if(number != null && number.isEmpty()==false)
        this.number = number;
    else
        throw new IllegalArgumentException("Input cannot be null or empty.");

    if(coordinates == null || coordinates.x < 0 || coordinates.y < 0)
        throw new IllegalArgumentException();
    else
        this.coordinates = coordinates;

}

public String getNumber()
{
    return this.number;
}
public Integer getNumberAsNumber()
{
    return Integer.parseInt(this.number);
}

public Point getCoordinates()
{
    return this.coordinates;
}
public int getX()
{
    return this.coordinates.x;
}
public int getY()
{
    return this.coordinates.y;
}

}

Шахматная фигура

package PhoneChess;

import java.util.HashMap;
import java.util.List;

public abstract class ChessPiece implements Movement {

protected String name = "";
protected HashMap<PadNumber, List<PadNumber>> moves = null;
protected Integer fullNumbers = 0;
protected int[] movesFrom = null;
protected PadNumber[][] thePad = null;
}

Интерфейс движения:

package PhoneChess;

import java.util.List;

public interface Movement 
{
public Integer findNumbers(PadNumber start, Integer digits);
public abstract boolean canMove(PadNumber from, PadNumber to);
public List<PadNumber> allowedMoves(PadNumber from);
public Integer countAllowedMoves(PadNumber from);
}

КусокФабрика

package PhoneChess;

public class PieceFactory 
{
    public ChessPiece getPiece(String piece, PadNumber[][] thePad)
    {
    if(thePad == null || thePad.length == 0 || thePad[0].length == 0)
        throw new IllegalArgumentException("Invalid pad");
    if(piece == null)
        throw new IllegalArgumentException("Invalid chess piece");

    if(piece.equalsIgnoreCase("Knight"))
        return new Knight("Knight", thePad);
    else
        return null;
}
}

Рыцарский класс

package PhoneChess;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public final class Knight extends ChessPiece implements Movement {

/**Knight movements
 * One horizontal, followed by two vertical
 * Or 
 * One vertical, followed by two horizontal
 * @param name
 */

public Knight(String name, PadNumber[][] thePad)
{
    if(name == null || name.isEmpty() == true)
        throw new IllegalArgumentException("Name cannot be null or empty");

    this.name = name;
    this.thePad = thePad;
    this.moves = new HashMap<>();
}


private Integer fullNumbers = null;

@Override
public Integer findNumbers(PadNumber start, Integer digits) 
{
    if(start == null || "*".equals(start.getNumber()) || "#".equals(start.getNumber()) ) { throw new IllegalArgumentException("Invalid start point"); }
    if(start.getNumberAsNumber() == 5) { return 0; } //Consider adding an 'allowSpecialChars' condition
    if(digits == 1) { return 1; };

    //Init
    this.movesFrom = new int[thePad.length * thePad[0].length];
    for(int i = 0; i < this.movesFrom.length; i++)
        this.movesFrom[i] = -1;

    fullNumbers = 0;
    findNumbers(start, digits, 1);      
    return fullNumbers;
}

private void findNumbers(PadNumber start, Integer digits, Integer currentDigits)
{
    //Base condition
    if(currentDigits == digits)
    {
        //Reset
        currentDigits = 1; 
        fullNumbers++; 
        return; 
    }
    if(!this.moves.containsKey(start))
        allowedMoves(start);

    List<PadNumber> options = this.moves.get(start);
    if(options != null)
    {
        currentDigits++; //More digits to be got
        for(PadNumber option : options)
            findNumbers(option, digits, currentDigits);
    }
}

@Override
public boolean canMove(PadNumber from, PadNumber to) 
{
    //Is the moves list available?
    if(!this.moves.containsKey(from.getNumber()))
    {
        //No? Process.
        allowedMoves(from);
    }
    if(this.moves.get(from) != null)
    {
        for(PadNumber option : this.moves.get(from))
        {
            if(option.getNumber().equals(to.getNumber()))
                return true;
        }
    }
    return false;

}

/***
 * Overriden method that defines each Piece's movement restrictions.
 */
@Override
public List<PadNumber> allowedMoves(PadNumber from) 
{
    //First encounter
    if(this.moves == null)
        this.moves = new HashMap<>();


    if(this.moves.containsKey(from))
        return this.moves.get(from);
    else
    {
        List<PadNumber> found = new ArrayList<>();
        int row = from.getY();//rows
        int col = from.getX();//columns

        //Cases:
        //1. One horizontal move each way followed by two vertical moves each way
        if(col-1 >= 0 && row-2 >= 0)//valid
        {
            if(thePad[row-2][col-1].getNumber().equals("*") == false && 
                    thePad[row-2][col-1].getNumber().equals("#") == false)
            {
                found.add(thePad[row-2][col-1]);
                this.movesFrom[from.getNumberAsNumber()] = this.movesFrom[from.getNumberAsNumber()] + 1;
            }

        }
        if(col-1 >= 0 && row+2 < thePad.length)//valid
        {
            if(thePad[row+2][col-1].getNumber().equals("*") == false && 
                    thePad[row+2][col-1].getNumber().equals("#") == false)
            {
                found.add(thePad[row+2][col-1]);
                this.movesFrom[from.getNumberAsNumber()] = this.movesFrom[from.getNumberAsNumber()] + 1;
            }
        }
        if(col+1 < thePad[0].length && row+2 < thePad.length)//valid
        {
            if(thePad[row+2][col+1].getNumber().equals("*") == false && 
                    thePad[row+2][col+1].getNumber().equals("#") == false)
            {
                found.add(thePad[row+2][col+1]);
                this.movesFrom[from.getNumberAsNumber()] = this.movesFrom[from.getNumberAsNumber()] + 1;
            }
        }
        if(col+1 < thePad[0].length && row-2 >= 0)//valid
        {
            if(thePad[row-2][col+1].getNumber().equals("*") == false && 
                    thePad[row-2][col+1].getNumber().equals("#") == false)
            found.add(thePad[row-2][col+1]);
        }
        //Case 2. One vertical move each way follow by two horizontal moves each way

        if(col-2 >= 0 && row-1 >= 0)
        {
            if(thePad[row-1][col-2].getNumber().equals("*") == false && 
                    thePad[row-1][col-2].getNumber().equals("#") == false)
            found.add(thePad[row-1][col-2]);
        }
        if(col-2 >= 0 && row+1 < thePad.length)
        {
            if(thePad[row+1][col-2].getNumber().equals("*") == false && 
                    thePad[row+1][col-2].getNumber().equals("#") == false)
            found.add(thePad[row+1][col-2]);
        }

        if(col+2 < thePad[0].length && row-1 >= 0)
        {
            if(thePad[row-1][col+2].getNumber().equals("*") == false && 
                    thePad[row-1][col+2].getNumber().equals("#") == false)
            found.add(thePad[row-1][col+2]);
        }
        if(col+2 < thePad[0].length && row+1 < thePad.length)
        {
            if(thePad[row+1][col+2].getNumber().equals("*") == false && 
                    thePad[row+1][col+2].getNumber().equals("#") == false)
            found.add(thePad[row+1][col+2]);
        }

        if(found.size() > 0)
        {
            this.moves.put(from, found);
            this.movesFrom[from.getNumberAsNumber()] = found.size();
        }
        else
        {
            this.moves.put(from, null); //for example the Knight cannot move from 5 to anywhere
            this.movesFrom[from.getNumberAsNumber()] = 0;
        }
    }

    return this.moves.get(from);


}

@Override
public Integer countAllowedMoves(PadNumber from) 
{
    int start = from.getNumberAsNumber();

    if(movesFrom[start] != -1)
        return movesFrom[start];
    else
    {
        movesFrom[start] = allowedMoves(from).size();
    }
    return movesFrom[start];
}

@Override
public String toString()
{
    return this.name;
}

}

Класс абитуриентов PhoneChess

package PhoneChess;


public final class PhoneChess 
{
private ChessPiece thePiece = null;
private PieceFactory factory = null;

public ChessPiece ThePiece()
{
    return this.thePiece;
}

public PhoneChess(PadNumber[][] thePad, String piece)
{
    if(thePad == null || thePad.length == 0 || thePad[0].length == 0)
        throw new IllegalArgumentException("Invalid pad");
    if(piece == null)
        throw new IllegalArgumentException("Invalid chess piece");

    this.factory = new PieceFactory();
    this.thePiece = this.factory.getPiece(piece, thePad);
}

public Integer findPossibleDigits(PadNumber start, Integer digits)
{
    if(digits <= 0)
        throw new IllegalArgumentException("Digits cannot be less than or equal to zero");

    return thePiece.findNumbers(start, digits);
}

public boolean isValidMove(PadNumber from, PadNumber to)
{
    return this.thePiece.canMove(from, to);
}

}

Код драйвера:

public static void main(String[] args) {


    PadNumber[][] thePad = new PadNumber[4][3];
    thePad[0][0] = new PadNumber("1", new Point(0,0));
    thePad[0][1] = new PadNumber("2", new Point(1,0));
    thePad[0][2] = new PadNumber("3",new Point(2,0));
    thePad[1][0] = new PadNumber("4",new Point(0,1));
    thePad[1][1] = new PadNumber("5",new Point(1,1));
    thePad[1][2] = new PadNumber("6", new Point(2,1));
    thePad[2][0] = new PadNumber("7", new Point(0,2));
    thePad[2][1] = new PadNumber("8", new Point(1,2));
    thePad[2][2] = new PadNumber("9", new Point(2,2));
    thePad[3][0] = new PadNumber("*", new Point(0,3));
    thePad[3][1] = new PadNumber("0", new Point(1,3));
    thePad[3][2] = new PadNumber("#", new Point(2,3));

    PhoneChess phoneChess = new PhoneChess(thePad, "Knight");
    System.out.println(phoneChess.findPossibleDigits(thePad[0][1],4));
}

}
person Fayez    schedule 28.01.2016

Это можно сделать за O(log N). Рассмотрите клавиатуру и возможные ходы на ней как граф G(V, E), где вершины — это доступные цифры, а ребра говорят, какие цифры могут следовать за какими. Теперь для каждой выходной позиции i мы можем сформировать вектор Paths(i), содержащий количество различных путей, которыми может быть достигнута каждая вершина. Теперь довольно легко увидеть, что для для заданной позиции i и цифры v возможные пути, по которым можно пройти, представляют собой сумму различных путей, по которым можно было пройти возможные предшествующие цифры, или Paths(i)[v] = sum(Paths(i-1)[v2] * (1, если (v,v2) в E, иначе 0) для v2 в V ). Теперь это сумма каждой позиции предыдущего вектора, умноженная на соответствующую позицию в столбце матрицы смежности. Таким образом, мы можем упростить это как Paths(i) = Paths(i-1) · A, где A — матрица смежности графа. Избавившись от рекурсии и воспользовавшись ассоциативностью матричного умножения, получаем Paths(i) = Paths(1) · A^(i-1). Мы знаем Пути(1): у нас есть только один путь к цифре 1.

Общее количество путей для n-значного числа равно сумме путей для каждой цифры, поэтому окончательный алгоритм выглядит следующим образом: TotalPaths(n) = sum( [1,0,0,0,0,0,0 ,0,0,0] · A^(n-1) )

Возведение в степень можно вычислить путем возведения в квадрат за время O(log(n)), при заданном постоянном умножении времени, в противном случае O(M(n) * log(n)), где M(n) — это сложность вашего любимого алгоритма умножения с произвольной точностью для n цифр.

person Ants Aasma    schedule 25.05.2010
comment
Хорошее использование матрицы смежности. Но обратите внимание, что вы считаете N длиной телефонного номера, тогда как в вопросе сложность заключается в количестве доступных цифр. При этом вы получаете O (n ^ 2) для вычисления A в 10-й степени. - person Tomer Vromen; 30.05.2010
comment
Нет, я думаю, что N должна быть длиной телефонного номера. Как клавиатура должна искать большее количество цифр? - person aioobe; 30.05.2010
comment
Сложность клавиатуры произвольного размера с произвольными ходами будет основываться на наивном умножении разреженных матриц O(dmlog n), где d — количество цифр, а m — общее количество возможных ходов (m ‹= д ^ 2). Клавиатура на основе сетки будет иметь O (d) возможных ходов, поэтому да, если бы вопрос касался размера клавиатуры, тогда этот алгоритм был бы квадратичным. - person Ants Aasma; 31.05.2010
comment
На самом деле это решение может выполняться за постоянное время относительно N. Поскольку A симметрично, его можно диагонализовать, и тогда взятие A ^ N становится вопросом возведения 10 действительных чисел в N-ю степень, которую можно принять за постоянное время. , и выполнение некоторых других операций, не зависящих от N. - person Nir Friedman; 28.12.2015

Более простой ответ.

#include<stdio.h>

int a[10] = {2,2,2,2,3,0,3,2,2,2};
int b[10][3] = {{4,6},{6,8},{7,9},{4,8},{0,3,9},{},{1,7,0},{2,6},{1,3},{2,4}};

int count(int curr,int n)
{
    int sum = 0;
    if(n==10)
        return 1;
    else
    {
        int i = 0;
        int val = 0;
        for(i = 0; i < a[curr]; i++)
        {
            val = count(b[curr][i],n+1);
            sum += val;
        }
        return sum;
    }
}

int main()
{
    int n = 1;
    int val = count(1,0);
    printf("%d\n",val);
}

праздновать!!

person Boolean    schedule 29.05.2010
comment
Разве n==10 не должно быть равно 9, так как вы должны начать с 1 в качестве первой цифры, и вам нужно всего 9 цифр, чтобы получить 10-значное число? - person Sarp Centel; 07.03.2011

Решение для постоянной времени выполнения:

#include <iostream>

constexpr int notValid(int x, int y) {
return !(( 1 == x && 3 == y ) || //zero on bottom.
         ( 0 <= x && 3 > x && //1-9
           0 <= y && 3 > y ));
}

class Knight {
    template<unsigned N > constexpr int move(int x, int y) {
        return notValid(x,y)? 0 : jump<N-1>(x,y);
    }

    template<unsigned N> constexpr int jump( int x, int y ) {
        return  move<N>(x+1, y-2) +
            move<N>(x-1, y-2) +
            move<N>(x+1, y+2) +
            move<N>(x-1, y+2) +
            move<N>(x+2, y+1) +
            move<N>(x-2, y+1) +
            move<N>(x+2, y-1) +
            move<N>(x-2, y-1);
    }

public:
    template<unsigned N> constexpr int count() {
        return move<N-1>(0,1) + move<N-1>(0,2) +
            move<N-1>(1,0) + move<N-1>(1,1) + move<N-1>(1,2) +
            move<N-1>(2,0) + move<N-1>(2,1) + move<N-1>(2,2);
    }
};

template<> constexpr int Knight::move<0>(int x, int y) { return notValid(x,y)? 0 : 1; }
template<> constexpr int Knight::count<0>() { return 0; } //terminal cases.
template<> constexpr int Knight::count<1>() { return 8; }


int main(int argc, char* argv[]) {
    static_assert( ( 16 == Knight().count<2>() ), "Fail on test with 2 lenght" );  // prof of performance
    static_assert( ( 35 == Knight().count<3>() ), "Fail on test with 3 lenght" );

    std::cout<< "Number of valid Knight phones numbers:" << Knight().count<10>() << std::endl;
    return 0;
}
person VladimirS    schedule 17.11.2015
comment
Ну, а если поставить количество цифр (10) прямо в коде, то с таким же успехом можно сразу сделать std::cout << 1424 << std::endl;. :-) (Я предполагаю, что это решение не работает, если вы читаете N со стандартного ввода.) - person aioobe; 04.04.2016
comment
@aioobe Предполагается, что это сработает, если вы передадите этот N компилятору stdin :) - person VladimirS; 05.04.2016

Метод возвращает список из 10-значных чисел, начинающихся с 1. Счет снова равен 1424.

  public ArrayList<String> getList(int digit, int length, String base ){
    ArrayList<String> list = new ArrayList<String>();
    if(length == 1){
        list.add(base);
        return list;
    }
    ArrayList<String> temp;

    for(int i : b[digit]){
        String newBase = base +i;
        list.addAll(getList(i, length -1, newBase ));
    }
    return list;
}
person navin    schedule 04.04.2016

Не уверен, что я что-то пропустил, но читая описание проблемы, я пришел к этому решению. Он имеет временную сложность O(n) и пространственную сложность O(1).

Я понял, что номер 1 находится на углу, верно? В каждом углу вы можете двигаться либо в одну из сторон (4 из 9 и 3, или 6 из 7 в 1), либо в одну из «вертикальных» сторон (8 из 3 и 1, или 2 из 9 и 7). Таким образом, углы добавляют два движения: боковое и вертикальное. Это верно для всех четырех углов (1,3,9,7).

С каждой стороны вы можете либо перейти к двум углам (7 и 1 из 6, 9 и 3 из 4), либо добраться до нижней клавиши (0). Это три хода. Два угла и одно дно.

По нижней клавише (0) можно перемещаться в обе стороны (4 и 6). Таким образом, на каждом шаге вы проверяете все возможные окончания пути предыдущей длины (то есть, сколько окончилось на углу, стороне, «вертикали» или «нижнем» нулевом ключе), а затем генерируете новое окончание. рассчитывается в соответствии с правилами генерации, изложенными ранее.

  • Каждое окончание угла добавляет сторону и вертикаль.
  • Каждое боковое окончание добавляет 2 угла и дно.
  • Каждое вертикальное окончание добавляет 2 угла.
  • Каждое нижнее окончание добавляет 2 стороны.

генерация возможностей из предыдущих шагов

Если вы начинаете с клавиши «1», вы начинаете с одного возможного углового решения, на каждом шаге вы подсчитываете количество угловых, боковых, вертикальных и нижних окончаний предыдущего шага, а затем применяете правила для создания следующего подсчета.

В простом коде javascript.

function paths(n) {
    //Index to 0
    var corners = 1;
    var verticals = 0;
    var bottom = 0;
    var sides = 0;

    if (n <= 0) {
        //No moves possible for paths without length 
        return 0;
    }

    for (var i = 1; i < n; i++) {
        var previousCorners = corners;
        var previousVerticals = verticals;
        var previousBottom = bottom;
        var previousSides = sides;

        sides = 1 * previousCorners + 2 * previousBottom;
        verticals = 1 * previousCorners;
        bottom = 1 * previousSides;
        corners = 2 * previousSides + 2 * previousVerticals;
        //console.log("Moves: %d, Length: %d, Sides: %d, Verticals: %d, Bottom: %d, Corners: %d, Total: %d", i, i + 1, sides, verticals, bottom, corners, sides+verticals+bottom+corners);  
    }

    return sides + verticals + bottom + corners;

}

for (var i = 0; i <= 10; i++) {
    console.log(paths(i));  
}
person Patricio Marrone    schedule 11.04.2016
comment
Хорошее наблюдение и решение. Я думаю, что это похоже на восходящий подход к динамическому программированию, когда вы поддерживаете только предыдущую строку. - person boni; 16.11.2016

Эта проблема также может быть смоделирована как проблема удовлетворения ограничений (сокращенно CSP).

Я предлагаю использовать решатель Minion (быстрый и масштабируемый), который вы можете найти здесь .

Моделирование может быть утомительным и занимать много времени (крутая кривая обучения).

Вместо использования ввода на языке Minion я советую сформулировать модель с помощью независимого от решателя языка моделирования, такого как ESSENCE и найти соответствующий конвертер.

person menjaraz    schedule 14.12.2011

Рекурсивный подход к запоминанию:

vector<vector<int>> lupt = { {4, 6}, {6, 8}, {9, 7}, {4, 8}, {3, 9, 0},
                             {},     {1,7,0}, {6, 2}, {1, 3}, {2, 4} };

int numPhoneNumbersUtil(int startdigit, int& phonenumberlength, int currCount, map< pair<int,int>,int>& memT)
{
    int noOfCombs = 0;
    vector<int> enddigits;

    auto it = memT.find(make_pair(startdigit,currCount));
    if(it != memT.end())
    {
        noOfCombs = it->second;
        return noOfCombs;
    }

    if(currCount == phonenumberlength)
    {
        return 1;
    }

    enddigits = lupt[startdigit];
    for(auto it : enddigits)
    {
        noOfCombs += numPhoneNumbersUtil(it, phonenumberlength, currCount + 1, memT);
    }

    memT.insert(make_pair(make_pair(startdigit,currCount), noOfCombs));
    return memT[make_pair(startdigit,currCount)];

}

int numPhoneNumbers(int startdigit, int phonenumberlength)
{
    map<pair<int,int>,int> memT;
    int currentCount = 1; //the first digit has already been added
    return  numPhoneNumbersUtil(startdigit, phonenumberlength, currentCount, memT);
}
person Raghav Navada    schedule 21.02.2017

Я реализовал как брутфорс, так и модели динамического программирования

import queue


def chess_numbers_bf(start, length):
    if length <= 0:
        return 0
    phone = [[7, 5], [6, 8], [3, 7], [9, 2, 8], [], [6, 9, 0], [1, 5], [0, 2], [3, 1], [5, 3]]
    total = 0
    q = queue.Queue()
    q.put((start, 1))

    while not q.empty():
        front = q.get()
        val = front[0]
        len_ = front[1]
        if len_ < length:
            for elm in phone[val]:
                q.put((elm, len_ + 1))
        else:
            total += 1
    return total


def chess_numbers_dp(start, length):
    if length <= 0:
        return 0

    phone = [[7, 5], [6, 8], [3, 7], [9, 2, 8], [], [6, 9, 0], [1, 5], [0, 2], [3, 1], [5, 3]]
    memory = {}

    def __chess_numbers_dp(s, l):
        if (s, l) in memory:
            return memory[(s, l)]
        elif l == length - 1:
            memory[(s, l)] = 1
            return 1
        else:
            total_n_ways = 0
            for number in phone[s]:
                total_n_ways += __chess_numbers_dp(number, l+1)
            memory[(s, l)] = total_n_ways
            return total_n_ways
    return __chess_numbers_dp(start, 0)


# bf
for i in range(0, 10):
    print(i, chess_numbers_bf(3, i))
print('\n')

for i in range(0, 10):
    print(i, chess_numbers_bf(9, i))
print('\n')

# dp
for i in range(0, 10):
    print(i, chess_numbers_dp(3, i))
print('\n')

# dp
for i in range(0, 10):
    print(i, chess_numbers_dp(9, i))
print('\n')
person onesiumus    schedule 27.12.2017

Рекурсивная функция в Java:

public static int countPhoneNumbers (int n, int r, int c) {
        if (outOfBounds(r,c)) {
            return 0;
        } else {
            char button = buttons[r][c];
            if (button  == '.') {
                // visited
                return 0;
            }  else {
                buttons[r][c] = '.'; // record this position so don't revisit.
                // Count all possible phone numbers with one less digit starting
                int result=0;
                result = countPhoneNumbers(n-1,r-2,c-1)
                                         + countPhoneNumbers(n-1,r-2,c+1)
                                         + countPhoneNumbers(n-1,r+2,c-1)
                                         + countPhoneNumbers(n-1,r+2,c+1)
                                         + countPhoneNumbers(n-1,r-1,c-2)
                                         + countPhoneNumbers(n-1,r-1,c+2)
                                         + countPhoneNumbers(n-1,r+1,c-2)
                                         + countPhoneNumbers(n-1,r+1,c+2);
                }
                buttons[r][c] = button; // Remove record from position.
                return result; 
            }
        }
    }
person richluo    schedule 21.01.2016