大N的递归失败

时间:2016-04-19 12:33:43

标签: algorithm recursion dynamic-programming

我遇到了这个问题。

让我们考虑一个游戏,它有3个单元格宽的单元格 N 单元格。每个细胞都有一个 写在0和99之间的数字。你可以进入任何一个董事会 顶行的单元格,从底部的任何单元格退出。您 可以从一个单元格移动到下面一行中的任何相邻单元格(对角线或正下方)。 当您在单元格时,您必须添加或减去写入的数字 在细胞上。 对于给定值X,大于或等于的最小数量是多少 X,你可以从这个过程获得吗?

输入 第一行输入包含一个整数,即数字 测试用例。每个测试用例的第一行包含两个整数, N X 。 下一个 N 行每个包含三行 以空格分隔的数字,必须添加或减去的单元格值。

输出

对于每组输入打印,最小数字大于或等于 X

示例输入

2
3 0
83 86 77
15 93 35
86 92 49
3 59
83 86 77
15 93 35
86 92 49

示例输出

2 59

注意:

2 = 86 - 35 - 49

59 = 86 - 93 + 86

这是我的解决方案:

import java.util.Scanner;

public class n3 {

    static int min;
    static int result;
    static int X;
    int go = 0;

    public static void main(String args[]) throws Exception {
        {
            Scanner sc = new Scanner(System.in);
            // sc = new Scanner(new FileInputStream("input.txt"));

            int T = sc.nextInt();
            for (int tc = 0; tc < T; tc++) {



                int N = sc.nextInt();
                min = sc.nextInt();

                result = Integer.MAX_VALUE;

                int m[][] = new int[N][N];
                for (int i = 0; i < N; i++) {
                    for (int j = 0; j < 3; j++) {
                        m[i][j] = sc.nextInt();
                    }
                }

                n3 game = new n3();
                game.start(m, N);
                System.out.println(result);

            }
        }
    }

    private void start(int[][] m, int N) {

        int x = min;
        compute(m, -1, 0, 0, N);
        // compute(m, -1, 0, -x, N);
        compute(m, -1, 1, 0, N);
        compute(m, -1, 2, 0, N);

    }

    private boolean isSafe(int[][] m, int row, int col, int N) {

        if (row >= 0 && col >= 0 && row < N && col < 3) {
            return true;
        }

        return false;
    }

    private void compute(int[][] m, int x, int y, int value, int N) {
        // System.out.println(value + " " + x + " " + y);

        if (go == 1) {
            return;
        }

        if (x == N - 1 && value >= min) {
            if (value < result) {
                result = value;
            }

            if (value == min) {
                result = min;
                go = 1;
            }
            return;
        }

        if (isSafe(m, x + 1, y - 1, N)) {
            compute(m, x + 1, y - 1, value + m[x + 1][y - 1], N);
            compute(m, x + 1, y - 1, value - m[x + 1][y - 1], N);
        }

        if (isSafe(m, x + 1, y, N)) {
            compute(m, x + 1, y, value + m[x + 1][y], N);
            compute(m, x + 1, y, value - m[x + 1][y], N);
        }

        if (isSafe(m, x + 1, y + 1, N)) {
            compute(m, x + 1, y + 1, value + m[x + 1][y + 1], N);
            compute(m, x + 1, y + 1, value - m[x + 1][y + 1], N);
        }

    }
}

然而,当N很大时,这会失败。还有其他方法可以解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

每次调用compute(m,x,...)都会进行四次或六次计算调用(m,x + 1,...)。因此,如果将N增加1,则工作量至少增加4倍。您的代码将永远运行。

对于每一行和每列,都要跟踪您在该点可能具有的整数集。然后在递归中,检查您是否已经计算了该行/列的点,而不是再次计算。

答案 1 :(得分:0)

@ gnasher729有正确的想法。以下是您给出的示例的简短描述。

从电路板顶部开始。对于每个方格,移动并计算每个方格的可能总数。将这些填入列表清单;这一行的“状态”看起来像是:

left:  [-83, 83],
mid:   [-86, 86],
right: [-77, 77]

现在移动到第2行。中间位置可以连接到3个方块中的每一个;两端只能连接到较近的地方2.使用add&amp;减去每个正方形的运算。例如,左方(15)可以由左(83)或中(86)位置“馈送”。这一次,我将分两步完成:

left:  [-83-15, 83-15, -83+15, 83+15, -86-15, 86-15, -86+15, 86+15], 
mid:   [-83-93, 83-93, -83+93, 83+93, -86-93, 86-93, -86+93, 86+93, -77-93, 77-93, -77+93, 77+93]
right: [-86-35, 86-35, -86+35, 86+35, -77-35, 77-35, -77+35, 77+35]

当我们进行数学计算时,我们得到条目:

[-98, 68, -68, 98, -101, 71, -71, 101],
[-176, -10, 10, 176, -179, -7, 7, 179, -170, -16, 16, 170],
[-121, 51, -51, 121, -112, 42, -42, 112]

继续沿着桌子的行向前走。由于您不关心路径,只关注总和本身,您不必跟踪所有小计 - 只是最近一行的左,中,右集。

当你到达底部时,合并三个列表,按升序排序,然后取第一个至少与 X 一样大的数字。