Грубая сила лягушка Прыжки игра



Последняя вариация прыжка лягушки показана в концевидео .

Короче говоря, у вас есть n количество лилий в строке и одна лягушка на каждый из них.

В последнем варианте (тот, который я хочу перебить силой), второй первая и вторая последние лилии не имеют лягушки. Ваша цель состоит в том, чтобы соберите всех лягушек в одну Лилию. Каждая лягушка может прыгать вправо или влево основываясь на количестве лягушек на его лилии, но не может прыгать на пустой лилейник.
(площадку с 1 движется лягушка 1 место, коврик с N лягушек движется только Н пятна)

Пример решения для n=12: (ниже 12 решений нет)

[1,0,1,1,1,1,1,1,1,1,0,1] - начало формирования лягушек. (подсчет лягушки от 0. до 11.)
[1,0,1,0,2,1,1,1,1,1,0,1] - лягушка 3. подскочивший справа
[1,0,1,0,2,1,2,0,1,1,0,1] - лягушка 7. прыгнул влево
[1,0,1,0,4,1,0,0,1,1,0,1] - лягушки 6. прыгнул влево
[5,0,1,0,0,0,0,0,1,1,0,1] - Лягушки 4. прыгнул влево
[0,0,1,0,0,6,0,0,1,1,0,1] - лягушки 0. прыгнул вправо
[0,0,1,0,0,0,0,0,0,1,1,0,7] - лягушки 5. прыгнул вправо
[0,0,1,0,0,0,0,0,0,0,2,0,7] - лягушки 8. прыгнул вправо
[0,0,1,0,0,0,0,0,0,0,0,9] - лягушки 9. прыгнул вправо
[0,0,10,0,0,0,0,0,0,0,0,0,0] - лягушки 11. прыгнул влево-решил

Я хочу найти решения для n лягушек, если решение существует.Я знаю от руки, что 12,14,15,16,17,18,19,20 имеют по крайней мере одно решение и что 1-11 и 13 не имеют решения.

Я попытался написать фрагмент кода, который будет проходить через все комбинации, чтобы найти решение для n лилий.

EDIT: код теперь работает, благодаря OleV.V. , также добавлено ведение журнала.

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;

// # Brute Force # Brute Force # Brute Force # Brute Force # Brute Force # //
public class Frogger {

    /** 
     * PUBLIC STATIC GLOBAL VARIABLES - Modify these as you wish.
     * 
     * Time Data: Levels < 20 ~ around couple seconds
     *            Level = 20 ~ around a minute
     *            Level = 21 ~ around a quarter of an hour
     *            Level = 22 ~ around a sixth of a minute
     *            Level = 23 ~ around half an hour
     *            Level = 24 ~ around a minute
     * 
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
        public static int Level = 12;
        public static boolean LogSolution = true;
        public static boolean LogAll = false;
    /** * * * * * * * * * * * * * * * * * * * * * * * * * * * */

    // used for logging
    private static Deque<Jump> solution = new ArrayDeque<>(Level);
    private static double time;

    public static void main(String[] args) {

        // log the time
        time = System.currentTimeMillis();

        // build the world & start jumping
        run(Level);
    }

    public static void run(int n) {

        // create the world
        int[] world = new int[n];
        for (int i = 0; i < n; i++) {
            world[i] = 1;
        }
        world[1] = 0;
        world[n-2] = 0;

        // start jumping
        if (Level > 11 && Level != 13) jump(world);
        else System.out.println("Unsolvable");
    }


    //////////////////////////////////////////////////////

    public static void jump(int[] world) {

    for (int i = 0; i < world.length; i++) {

        if (world[i] != 0) { // pad has a frog

            // check if it is solved at current pad
            if (world[i] == Level - 2) {
                System.out.println("SOLUTION: " + Arrays.toString(world));
                System.out.println(solution);
                System.out.println("n" + (System.currentTimeMillis() - time) / 1000 + " seconds");
                System.exit(0);
            }   

            // roll-back var
            int temp = 0;

            // attempts to make a RIGHT jump 

                if (world[i] + i < world.length) { // right jump is in bound
                    if (world[i + world[i]]  != 0) { // can't jump on empty pad

                        temp = world[i];

                        // jump RIGHT
                        world[i + world[i]] += world[i];
                        world[i] = 0;

                        solution.push(new Jump(temp, i, i + temp)); // log the solution step 1/2
                        if (LogSolution) if (LogAll) System.out.println( "J: " + Arrays.toString(world)); // log the jump

                        // continue jumping
                        jump(world); 

                        // roll-back right jump
                        world[i] = temp;
                        world[i + world[i]] -= world[i];

                        if (LogAll) System.out.println("R: " + Arrays.toString(world)); // log the rollback
                        if (LogSolution) solution.pop(); // log the solution step 2/2
                    }
                }   

            // attempts to make a LEFT jump 

                if (i - world[i] >= 0) { // left jump is in bound
                    if (world[i - world[i]]  != 0) { // can't jump on empty pad

                        temp = world[i];

                        // jump LEFT
                        world[i - world[i]] += world[i];
                        world[i] = 0;

                        if (LogSolution) solution.push(new Jump(temp, i, i - temp)); // log the solution step 1/2
                        if (LogAll) System.out.println("J:" + Arrays.toString(world)); // log the jump

                        // continue jumping
                        jump(world); 

                        // roll-back left jump
                        world[i] = temp;
                        world[i - world[i]] -= world[i];

                        if (LogAll) System.out.println("R: " + Arrays.toString(world)); // log the rollback
                        if (LogSolution) solution.pop(); // log the solution step 2/2
                    }
                }
        }
    }
    }

}

Примечание: эта задача была математически решена для всех разрешимых n (все n > 11, кроме 13, имеют решение, достижимое обобщенным методом). Этот кусок кода-всего лишь моя попытка. чтобы сделать некоторую рекурсию в java.

1 2

1 ответ:

Рад, что ты получил его на работу. Я не думаю, что вам нужен мой код сейчас, но я дам в конце этого ответа на всякий случай.

Во-первых, как регистрировать решение? Я думаю, вы думаете, что знать, что конечный результат был [0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0], не так уж интересно; мы хотели бы знать, как он был получен. Я представлю два способа.

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

private static Deque<Jump> solution = new ArrayDeque<>(Level);

(java.util.ArrayDeque является рекомендуемым классом как для стеков, так и для очередей; для стека ArrayList - другой вариант.) Теперь в вашем коде, где сказано log the jump do

                    solution.push(new Jump(temp, i, i + temp));

At log the rollback do

                    solution.pop();

Используйте простой вспомогательный класс Jump, который может, например, выглядеть следующим образом:

public class Jump {
    int count;
    int from;
    int to;

    public Jump(int count, int from, int to) {
        this.count = count;
        this.from = from;
        this.to = to;
    }

    @Override
    public String toString() {
        return "" + count + " frog/s jump from " + from + " to " + to;
    }
}
Когда я попробовал его в своем решении, один поиск занял на 20 % больше времени. Я бы сказал, что это приемлемо. Если вы очень обеспокоены производительностью, просто войдите в свой путь из-за того, что нашел решение. Это потребует, чтобы вы возвратили логическое значение, чтобы указать успех, а не использовать System.exit () для остановки поиска. Теперь ваш рекурсивный вызов становится:
                    if (jump(world)) {
                        solution.push(new Jump(temp, i, i + temp));
                        return true;
                    }

Вы получаете элементы в стеке решений в обратном порядке, чем раньше, я ожидаю, что вы поймете это. Также вместо System.exit(0); делайте return true;. В нижней части метода верните false. Я не измерял влияние на производительность, но я ожидаю, что оно будет незначительным по сравнению с регистрацией ничего. Сейчас вы можете получить вывод типа:

1 frog/s jump from 3 to 4
1 frog/s jump from 7 to 6
2 frog/s jump from 6 to 4
4 frog/s jump from 4 to 0
5 frog/s jump from 0 to 5
6 frog/s jump from 5 to 11
1 frog/s jump from 8 to 9
2 frog/s jump from 9 to 11
9 frog/s jump from 11 to 2
Наконец, вот как я это сделал, просто для полноты картины. Я не заметил никаких интересных отличий от вашего кода.
public static void jump(int[] world) {
    for (int fromIndex = 0; fromIndex < world.length; fromIndex++) { // index of pad to jump from
        // any frog/s here?
        int frogsJumping = world[fromIndex];
        if (frogsJumping > 0) {
            // try to jump left; frogsJumping frogs jump frogsJumping places
            int targetIndex = fromIndex - frogsJumping;
            if (targetIndex >= 0 && world[targetIndex] > 0) { // must not jump to empty pad
                performJump(fromIndex, targetIndex, world, frogsJumping);
            }
            // try a right jump
            targetIndex = fromIndex + frogsJumping;
            if (targetIndex < world.length && world[targetIndex] > 0) {
                performJump(fromIndex, targetIndex, world, frogsJumping);
            }
        }
    }
}

private static void performJump(int fromIndex, int toIndex, int[] world, int frogsJumping) {
    solution.push(new Jump(frogsJumping, fromIndex, toIndex));
    world[fromIndex] = 0;
    world[toIndex] += frogsJumping;
    if (world[toIndex] == noOfFrogs) {
        System.out.println("Solved: " + Arrays.toString(world));
        System.exit(0);
    }
    jump(world);
    // backtrack
    world[toIndex] -= frogsJumping;
    world[fromIndex] = frogsJumping;
    solution.pop();
}