坚持递归算法

时间:2017-10-01 11:36:05

标签: java algorithm recursion

我有一个关于工作入职考试递归的问题,但我没有在2小时内完成。我很好奇在考试之后如何做到这一点,但我无法找到解决方案。

你可以想象有一个尺寸为n * m的硬币推动器(2D阵列)。 每个操作(向上或向下或向左或向右移动)将丢弃一行或一列硬币

这个问题要求我找到最短的可能动作,最后仍然是k币。如果最后不可能保留k个硬币,则返回-1

当有多个操作具有相同的最大硬币数量(相同的值被丢弃)时,我坚持如何确定下一步动作

我相信我需要递归计算,模拟所有未来可能的移动以确定当前的移动操作。

但是我不知道如何实现这个算法,任何人都可以帮忙吗?

谢谢!

Question : 
There is a rectangular chessboard containing N‘M cells. each of
which either has one coin or nothing.
You can move all the coins together in one direction (such as up,
down, left, and right), but each time you can move these coins by
only one cell.

If any coins fall out of the chessboard, they must be thrown away.
If it is required to keep K coins on the board, what is the minimum
moves you have to take?

Output -1 if you can not meet this requirement.

The first line of the input are two positive 
integers n, representing the size of the board.

For the next n line(s), each line has m numbers of 
characters, with 'o' indicating a coin, '.' indicates an empty grid.

The last line is a positive integer k, 
indicating the number of coins to be retained.

30% small input: 1 <= n,m <= 5, 0 < k < 25
40% medium input: 1 <= n,m <= 10, 0 < k < 100
30% large input: 1 <= n,m <= 100, 0 < k < 10000

sample input:
3 4
.o..
oooo
..o.
3

sample output:
2

我的临时回答

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

public class main {
    String[][] inputArray;

    int n;
    int m;
    int k;

    int totalCoin = 0;
    int step = 0;
    public static void main(String[] args) {
        main temp = new main();
        temp.readData();
    }

    public void readData() {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        inputArray = new String [n][m];

        sc.nextLine(); // skipping

        for (int i = 0; i < n; i++) {
            String temp = sc.nextLine();
            for (int j = 0; j < m; j++) {
                if ((temp.charAt(j) + "").equals("o")) totalCoin++;
                inputArray[i][j] = temp.charAt(j) + "";
            }
        }

        k = sc.nextInt();   
        int result = 0;

        if (totalCoin >= k) {
            result = findMaxAndMove();
            System.out.println(result);

        }

    }

    public String findNextMove() {
        Map<String,Integer> tempList = new HashMap<String,Integer>();
        tempList.put("up", up());
        tempList.put("down", down());
        tempList.put("left", left());
        tempList.put("right", right());

        Map.Entry<String, Integer> maxEntry = null;


        for (Entry<String,Integer> temp : tempList.entrySet()) {
            if (maxEntry == null || temp.getValue() > maxEntry.getValue()) {
                maxEntry = temp;
            }
        }

        Map<String,Integer> maxList = new HashMap<String,Integer>();
        for (Entry<String,Integer> temp : tempList.entrySet()) {
            if (temp.getValue() == maxEntry.getValue()) {
                maxList.put(temp.getKey(), temp.getValue());
            }
        }

//      return maxList.entrySet().iterator().next().getKey();

        if (maxList.size() > 1) {
            // how to handle this case when more than 1 operations has the same max value???????????
            return ??????????????
        }
        else {
            return maxList.entrySet().iterator().next().getKey();
        }



//      


    }

    public int findMaxAndMove() {

        int up = up();
        int down = down();
        int left = left();
        int right = right();

        if ((totalCoin - up) == k) {
            step++;
            return step;
        }

        if ((totalCoin - down) == k) {
            step++;
            return step;
        }

        if ((totalCoin - left) == k) {
            step++;
            return step;
        }

        if ((totalCoin - right) == k) {
            step++;
            return step;
        }

        if (totalCoin - up < k && totalCoin - down < k && totalCoin - left < k && totalCoin - right < k) return -1;
        else {
            switch (findNextMove()) {
                case "up" :
                    totalCoin -= up;
                    this.moveUp();
                    break;
                case "down" :
                    totalCoin -= down;
                    this.moveDown();
                    break;
                case "left" :
                    totalCoin -= left;
                    this.moveLeft();
                    break;
                case "right" :
                    totalCoin -= right();
                    this.moveRight();
                    break;
            }
            step++;
            return findMaxAndMove(); // going to next move
        }

    }

    public String[] createBlankRow() {
        String[] temp = new String[m];
        for (int i = 0; i < m; i++) {
            temp[i] = ".";
        }
        return temp;
    }


    public int up() {
        int coinCounter = 0;
        for (int i = 0; i < m; i++) {
            if (inputArray[0][i].equals("o")) {
                coinCounter++;
            }
        }


        return coinCounter;
    }

    public void moveUp() {
        // going up
        for (int i = 0; i < n - 1; i++) {
            inputArray[i] = inputArray[i + 1];
        }

        inputArray[n-1] = createBlankRow();

    }

    public int down() {
        int coinCounter = 0;
        for (int i = 0; i < m; i++) {
            if (inputArray[n-1][i].equals("o")) {
                coinCounter++;
            }
        }


        return coinCounter;
    }

    public void moveDown() {
        // going down
        for (int i = n-1; i > 1; i--) {
            inputArray[i] = inputArray[i - 1];
        }

        inputArray[0] = createBlankRow();


    }

    public int left() {
        int coinCounter = 0;
        for (int i = 0; i < n; i++) {
            if (inputArray[i][0].equals("o")) {
                coinCounter++;
            }
        }

        return coinCounter;
    }

    public void moveLeft() {
        // going left
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m-1; j++) {
                inputArray[i][j] = inputArray[i][j+1];
            }
            inputArray[i][m-1] = ".";

        }


    }


    public int right() {
        int coinCounter = 0;
        for (int i = 0; i < n; i++) {
            if (inputArray[i][m-1].equals("o")) {
                coinCounter++;
            }
        }

        return coinCounter;
    }

    public void moveRight() {
        // going right
        for (int i = 0; i < n; i++) {
            for (int j = m-1; j > 0; j--) {
                inputArray[i][j] = inputArray[i][j-1];
            }
            inputArray[i][0] = ".";
        }


    }



    public void printboard() {
         for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                System.out.print(inputArray[i][j]);
            }
            System.out.println();
        }

    }


}

3 个答案:

答案 0 :(得分:0)

我怀疑你没有找到解决问题的正确算法。为了找到解决方案,不仅所有具有特殊硬币数量的可到达电路板都值得关注。你必须建立一块可到达的板。此树中的每个节点都通过操作连接到其子节点。这是递归进入现场。你停止了

  • 当您达到目标时(将此分支标记为可能的解决方案)或
  • 当无法通过进一步的操作(剩下的硬币太少)无法达到目标时
  • 当下一个操作到达已经在该分支中访问过的董事会时。

在这棵树中,所有标记为可能解决方案的最短分支都是实际的解决方案。如果没有分支被标记为可能的解决方案,则没有解决方案,您必须输出-1

答案 1 :(得分:0)

这是我的解决方案

public class CoinsMover {


public static List<String> getMinMoves(Character[][] board, int k, List<String> moves) {

    if (!movesAreValid(moves, board)) {
        return null;
    }

    int currentAmountOfCoins = getCoinsOnBoard(board);

    // All good no need to move any thing
    if (currentAmountOfCoins == k) {
        moves.add("done");
        return moves;
    }

    // Moved to much wrong way
    if (currentAmountOfCoins < k) {
        return null;
    }


    List<String> moveRight = getMinMoves(moveRight(board), k, getArrayWithApendded(moves, "right"));
    List<String> moveLeft = getMinMoves(moveLeft(board), k, getArrayWithApendded(moves, "left"));
    List<String> moveUp = getMinMoves(moveUp(board), k, getArrayWithApendded(moves, "up"));
    List<String> moveDown = getMinMoves(moveDown(board), k, getArrayWithApendded(moves, "down"));

    List<List<String>> results = new ArrayList<>();
    if (moveRight != null) {
        results.add(moveRight);
    }

    if (moveLeft != null) {
        results.add(moveLeft);
    }

    if (moveUp != null) {
        results.add(moveUp);
    }

    if (moveDown != null) {
        results.add(moveDown);
    }

    if (results.isEmpty()) {
        return null;
    }

    List<String> result = results.stream().sorted(Comparator.comparing(List::size)).findFirst().get();

    return result;
}

private static boolean movesAreValid(List<String> moves, Character[][] board) {
    long ups = moves.stream().filter(m -> m.equals("up")).count();
    long downs = moves.stream().filter(m -> m.equals("down")).count();
    long lefts = moves.stream().filter(m -> m.equals("left")).count();
    long rights = moves.stream().filter(m -> m.equals("right")).count();

    boolean verticalIsFine = ups <= board.length && downs <= board.length;
    boolean horizontalIsFine = lefts <= board[0].length && rights <= board[0].length;
    return verticalIsFine && horizontalIsFine;
}

private static List<String> getArrayWithApendded(List<String> moves, String move) {
    List<String> result = new ArrayList<>(moves);
    result.add(move);
    return result;
}

private static Character[][] moveRight(Character[][] board) {
    Character result[][] = new Character[board.length][board[0].length];
    // Cleaning left column
    for (int i = 0; i < board.length; i++)
        result[i][0] = '.';

    for (int row = 0; row < board.length; row++) {
        for (int column = 0; column < board[row].length - 1; column++) {
            result[row][column + 1] = board[row][column];
        }
    }

    return result;
}

private static Character[][] moveLeft(Character[][] board) {
    Character result[][] = new Character[board.length][board[0].length];
    // Cleaning right column
    for (int i = 0; i < board.length; i++)
        result[i][board[i].length - 1] = '.';

    for (int row = 0; row < board.length; row++) {
        for (int column = 1; column < board[row].length; column++) {
            result[row][column - 1] = board[row][column];
        }
    }

    return result;
}

private static Character[][] moveDown(Character[][] board) {
    Character result[][] = new Character[board.length][board[0].length];
    // Cleaning upper row
    for (int i = 0; i < board[board.length - 1].length; i++)
        result[0][i] = '.';

    for (int row = board.length - 1; row > 0; row--) {
        result[row] = board[row - 1];
    }

    return result;
}

private static Character[][] moveUp(Character[][] board) {
    Character result[][] = new Character[board.length][board[0].length];
    // Cleaning upper row
    for (int i = 0; i < board[board.length - 1].length; i++)
        result[board.length - 1][i] = '.';

    for (int row = 0; row < board.length - 1; row++) {
        result[row] = board[row + 1];
    }

    return result;
}

private static int getCoinsOnBoard(Character[][] board) {
    int result = 0;
    for (int i = 0; i < board.length; i++) {
        for (int j = 0; j < board[i].length; j++) {
            if (board[i][j] == 'o') {
                result++;
            }
        }
    }
    return result;
}

public static void main(String... args) {
    Character[][] mat = {{'.', 'o', '.', '.'}, {'o', 'o', 'o', 'o'}, {'.', '.', 'o', '.'}};
    List<String> result = getMinMoves(mat, 3, new ArrayList<>());
    if (result == null) {
        System.out.println(-1);//output [right, right, done]
    }

    System.out.println(result);
}

}

我承认我很难搜索重复内容,所以我使用的是一个字符串列表,它代表了你需要采取的路径才能找到解决方案。现在让我们先看一下如果当前移动无效则先停止条件返回null无效移动的例子是如果你有一个4列的表,你移动了5次相同的行和向上/向下移动。其次,如果董事会持有neede金额,我们就完成了。最后如果董事会持有少量我们失败了。所以现在要做的算法是在每个方向上寻找结果,然后从这里继续进行递归。

答案 2 :(得分:0)

您可以在下面找到解决方案。有几点需要注意。

  • 每当你看到一个问题提到在一个方向上移动一个数组时,定义一个可能的方向数组并在递归函数中循环它总是一个好主意。这可以防止你自己混淆。
  • 这个想法是按行和列计算硬币,这样你就可以在线性时间内找到剩余的硬币。
  • 剩下的工作就是遍历递归函数中的可能方向,以找到可能的解决方案。
  • 由于递归函数可以在一个圆圈中运行并返回到之前的某个位置,因此您应该/可以通过使用{{1}维护先前部分解的Map缓存来进一步改进递归函数}和curRowIdx为关键。
curColIdx