Минимальные шаги, чтобы выиграть Snake Ladder

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

**

Вот мое решение, но не уверен, что оно правильно или нет.

Эта проблема похожа на прыжок лягушки в массиве. Но до этого мы придется моделировать проблему в этом формате.

Создайте массив размером 100 и для каждого хранилища позиций 6, если есть нет змеи или лестницы. счетчик скачков магазина. если лестница присутствует в этом пункт. Если змея присутствует, тогда храните -ве прыжок в этом месте.

Теперь нам нужно решить, сколько минимальных шагов мы можем достичь до конца. Основная проблема может быть решена с помощью динамического программирования в O (n ^ 2) времени сложности и O (n) пространства.

Ответы

Ответ 1

Взгляните на этот пост в блоге, он выполняет полный математический анализ Chutes и Ladders, используя как моделирование Монте-Карло, так и цепи Маркова, Он показывает способ вычисления минимального количества шагов для выигрыша (в основном построить матрицу перехода и посмотреть, сколько раз вам нужно умножить стартовый вектор, чтобы получить решение, которое имеет ненулевую конечную позицию). Это, вероятно, не самый эффективный способ сделать это, но сообщение хорошо стоит прочитать.

Вот быстрое решение в python с использованием наборов. Я получил цифры в сканере из сообщения в блоге. На каждом шаге он просто вычисляет все позиции, достижимые с предыдущего шага, и продолжает делать это до тех пор, пока конечная позиция не окажется среди достижимых позиций:

jumps = {1: 38, 4: 14, 9: 31, 21: 42, 28: 84, 36: 44, 51: 67, 71: 91, 80: 100,
  98: 78, 95: 75, 93: 73, 87: 24, 64: 60, 62: 19, 56: 53, 49: 11, 48: 26, 16: 6}
final_pos = 100
positions = {0} #initial position off the board
nsteps = 0

while final_pos not in positions:
    nsteps += 1
    old_positions = positions
    positions = set()
    for pos in old_positions:
        for dice in range(1, 7):
            new_pos = pos + dice
            positions.add(jumps.get(new_pos, new_pos))

print 'Reached finish in %i steps' % nsteps            

Время выполнения пренебрежимо мало, и он выдает правильный ответ (см. блог) из 7.

Ответ 2

Здесь представлено простое решение поиска по ширине в Python:

# the target square and the positions of the snakes and ladders:
top = 100
jump = { 1: 38,  4: 14,  9: 31, 16:  6, 21: 42, 28: 84, 36: 44,
        48: 26, 49: 11, 51: 67, 56: 53, 62: 19, 64: 60, 71: 91,
        80:100, 87: 24, 93: 73, 95: 75, 98: 78}

# start from square 0 (= outside the board) after 0 rolls
open = {0}
path = {0: ()}

while len(open) > 0:
    i = open.pop()
    p = path[i] + (i,)
    for j in xrange(i+1, i+7):
        if j > top: break
        if j in jump: j = jump[j]
        if j not in path or len(path[j]) > len(p):
            open.add(j)
            path[j] = p

for i in path:
    print "Square", i, "can be reached in", len(path[i]), "rolls via", path[i]

Макет платы (т.е. словарь jump) берется из сообщения , связанного с Басом Суинкелем в его ответить. Этот код будет печатать (один из) самый короткий путь к каждому достижимому квадрату на доске, заканчивая:

Square 100 can be reached in 7 rolls via (0, 38, 41, 45, 67, 68, 74)

Если вы хотите получить полный выход, см. эту демонстрацию на ideone.com.

Ответ 3

Я реализовал это на С#. Вы можете проверить мой gist здесь. Я также вставлю код ниже.

В моей реализации я рассмотрел следующее:

  • избегать циклов. Это происходит, когда змея пересекает лестницу. Например, когда конец лестницы - голова змеи, а хвост змеи - начало той же лестницы. Это простой пример, но могут быть более сложные циклы.
  • взятие хороших змей: я заметил, что вам не нужно избегать всех змей. Иногда вам нужно взять змею, так как она поможет вам быстрее добраться до цели. Я называю их "хорошими змеями". Например, 3 > 60, затем 61 > 50, тогда 51 > 100 (цель). Если вы возьмете змею, количество минут кубика будет 3 и без змеи 8.
  • необязательные и обязательные прыжки. Если для необязательного перехода установлено значение false, алгоритм должен принимать прыжок, когда он попадает в один.
  • кратчайший путь осведомленности. Когда алгоритм попадает в цель, он регистрирует самый короткий путь, и во время вычисления он отбрасывает поиск, который длиннее текущего найденного решения. Это делает процесс намного быстрее в сложных ситуациях.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    namespace SnakeAndLaddersSolution
    {
      public class SnakeAndLadder
      {
        private struct Jump
       {
         public int Start;
         public int End;
       }
    
       private const int TOP = 100;
       private const int STEP_SIZE = 6;
       private int _minDiceCount = int.MaxValue;
       private readonly List<Jump> _jumps = new List<Jump>();
    
       public bool OptionalJump { get; set; }
    
       public void AddJump(int start, int end)
       {
           _jumps.Add(new Jump { Start = start, End = end });
       }
    
       public int Solve()
       {
           var path = new Stack<int>();
           path.Push(1); //start from square 1
           return FindMinimumDice(path, 0);
       }
    
       private int FindMinimumDice(Stack<int> path, int diceCount)
       {
           if (diceCount >= _minDiceCount)
           {
               //too long. we've already found a shortest path.
               //drop going deeper
               return -1;
           }
           var currentSquare = path.Peek();
           var diceCounts = new List<int>();
           int newDiceCount;
    
           var ignoreNormalJump = false;
    
        for (var i = 0; i <= STEP_SIZE; i++)
        {
            var newSquare = currentSquare + i;
            if (newSquare == TOP)
            {
                //got there
                var totalDiceCount = diceCount + (i == 0 ? 0 : 1);
                _minDiceCount = Math.Min(_minDiceCount, totalDiceCount); //register the shortest path
                return totalDiceCount;
            }
    
    
            if (_jumps.All(j => j.Start != newSquare)) continue; //only process jumps
    
            var jump = _jumps.First(j => j.Start == newSquare);
            if (path.Contains(jump.End))
                continue; //already been here
            path.Push(jump.Start);
            path.Push(jump.End);
            newDiceCount = FindMinimumDice(path, diceCount + (i == 0 ? 0 : 1));
            path.Pop();
            path.Pop();
            if (newDiceCount != -1)
                diceCounts.Add(newDiceCount);
            if (i == 0 && !OptionalJump) //the current squre is a jump that should be taken
            {
                ignoreNormalJump = true;
                break;
            }
        }
        if (!ignoreNormalJump)
        {
            var longestJump = 0;
            for (var i = STEP_SIZE; i > 0; i--)
            {
                if (_jumps.All(j => j.Start != currentSquare + i))
                {
                    longestJump = currentSquare + i;
                    break;
                }
            }
            if (longestJump != 0)
            {
                path.Push(longestJump);
                newDiceCount = FindMinimumDice(path, diceCount + 1);
                path.Pop();
                if (newDiceCount != -1)
                    diceCounts.Add(newDiceCount);
            }
        }
        return !diceCounts.Any() ? -1 : diceCounts.Min();
       }
      }
    
      class Program
      {
    
       static void Main(string[] args)
       {
           var sal = new SnakeAndLadder();
           //set OptionalJump to true if the jump is optional
           //sal.OptionalJump = true;
           sal.AddJump(10,60);
           sal.AddJump(51,100);
           Console.WriteLine(sal.Solve());
           Console.ReadLine();
       }
      }
      }
    

Ответ 4

Breadth First Search (BFS) или решение динамического программирования будут работать в O (N) раз, используя O (N) пространство.

Инициализация: Храните вспомогательный массив, чтобы держать лестницы и змеи. Предположим, что есть лестница от x-й до y-й ячейки. Итак, auxi [x] = y. Если есть змея от ячейки x до y, x>y, то продолжайте auxi[x]=-1. Если в текущей ячейке нет лестницы или змеи, держите auxi [x] = x;

Решение для динамического программирования:

res[top]=0;
for(int i  = top-1; i>=0; i--) {

    res[i] = INF;
    for(int j=1; j<=6; j++){

        if(i-j<0)break;

        if(auxi[i+j]>-1)     // if i+jth cell is start of a snake, we'll always skip it
        res[i]=min( res[i] , res[auxi[i+j]]+1 );
    }

}

Мы всегда будем пропускать ячейку, где начинается змея, потому что пусть предположим, что в ячейке x начинается змея, и она заканчивается на y-й ячейке, где y

Ответ 5

Решение в С# в o (n).

Постройте матрицу справки, чтобы перейти на каждый шаг и посмотреть, каков минимальный способ добраться туда и добавить ее к ней.

const int BoardSize = 100;
const int MaxStep = 6;
static void Main()
{

    // - means a ledder ending at the pos
    // + means a snake (taking you back n steps)
    int[] arr = new int[] {     
                            -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1, 
                            -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,
                            -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1, 
                            -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1, 
                            -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1, 
                            -1,     -1,     -1,     -1,      8,     -1,     -1,     -1,     -1,     -1, 
                            -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1, 
                            -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1, 
                            -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1,     -1, 
                            -1,     -1,     -1,     -1,     -1,     -1,     -1,     -71,    -1,     -1      };

    Console.WriteLine("Steps needed: " + solve(arr));
}

public static int solve(int[] inArr)
{
    int[] arr = new int[BoardSize];

    arr[0] = 1;

    for (int i = 0; i < BoardSize; ++i)
    {
        // steps here is the minimum of all the positions in all the previos 6 cells +1
        if (i < MaxStep)
            arr[i] = 1;
        else
        {
            int extraOption = int.MaxValue;
            if (inArr[i] < -1 || inArr[i] > 0)
                extraOption = arr[i + inArr[i]];
            else if (inArr[i] > 0)
                extraOption = arr[i + inArr[i]];
            arr[i] = min(arr[i - 1], arr[i - 2], arr[i - 3], arr[i - 4], arr[i - 5], arr[i - 6], extraOption) + 1;
        }
    }

    for (int i = 0; i < BoardSize; ++i)
    {
        Console.Write(arr[i] + "\t");
        if ((i + 1) % 10 == 0)
            Console.WriteLine("");
    }

    return arr[arr.Length-1];
}

public static int min(int a, int b, int c, int d, int e, int f, int g)
{
    int ab = Math.Min(a,b);
    int cd = Math.Min(c,d);
    int ef = Math.Min(e,f);
    return Math.Min(Math.Min(ab, cd), Math.Min(ef,g));
}

Ответ 6

Эта программа имитирует фактический сценарий... скажите мне, если это соответствует ожиданию или нет.

import java.util.HashMap;
import java.util.Map;

public class SnakeLadder {

    private Map<Integer,Integer> snakeLadderMapping=new HashMap<Integer,Integer>();


    private int winPosition=100;
    private int currentPosition=1;

    public SnakeLadder(){
        snakeLadderMapping.put(9, 19);
        snakeLadderMapping.put(17, 5);
        snakeLadderMapping.put(12, 40);
        snakeLadderMapping.put(24, 60);
        snakeLadderMapping.put(68, 89);
        snakeLadderMapping.put(50, 12);
        snakeLadderMapping.put(84, 98);
        snakeLadderMapping.put(75, 24);
        snakeLadderMapping.put(72, 16);
    }

    public int startGame(){
        int count=0;
        while(currentPosition!=winPosition){
            count++;
            getNextPosition(rollDice());
        }
        System.out.println("Game Won!!!!!!");
        return count;
    }

    public int rollDice(){
        return 1+ (int)(Math.random()*5);
    }

    public void getNextPosition(int diceValue){
        int temp=currentPosition+diceValue;
        if(snakeLadderMapping.containsKey(temp)){
            currentPosition=snakeLadderMapping.get(temp);
        }else{
            if(temp<=winPosition){
                currentPosition=temp;
            }
        }
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        SnakeLadder l=new SnakeLadder();
        System.out.println("No of Steps to win:"+l.startGame());

    }

}