在n x n网格中获得所有可能的下边缘和右边缘路径

时间:2017-08-10 13:14:33

标签: java algorithm

问题15:
从2×2网格的左上角开始,右下角有6条路线(没有回溯)。 通过20×20网格有多少条路线?

所以我对Problem 15的尝试有点粗暴,因为我试图通过从右到左和改变方向的第一次改变的前身来获得所有可能的有效路径的排列。例如,当我有一个2x2网格(查看问题15链接图形)时,我将采取的第一条路径是右 - 右 - 下 - 下,最后一条路径是下 - 右 - 右,这也是我的终止标准。我将可能的有效路径添加到列表中,并使用该列表来确定是否已添加有效路径。并且排列一条路径,我会做我前面提到过的事情:我在我的阵列中从右到左(图中的箭头指向的右下角)并改变第一个元素下一个元素与自身不同。因此,右 - 右 - 下 - 将变为右 - 右 - 下 - ,这显然是无效的,因为您必须拥有相同数量的权限能够到达终点。所以我想的是从左到右进行另一个循环,并根据需要更改尽可能多的元素以获得有效路径。因此,在此示例中,右 - 右 - 右 - 下变为向下 - 向右 - 向右 - 向下

另外,我忘了的是我没有计算点数,我正在计算从左上角到右下角的边缘。

所以我已经编写了一些代码,但它根本不起作用。

package projecteuler;

import java.util.ArrayList;

public class Projecteuler {
    public static final int GRIDSIZE = 2;

    public static void main(String[] args) {
        ArrayList<boolean[]> paths = new ArrayList<>();

        paths.add(new boolean[GRIDSIZE * 2]);
        for(int i = 0; i < GRIDSIZE; i++) {
            paths.get(0)[i] = true;
            paths.get(0)[GRIDSIZE * 2 - 1 - i] = false;
        }

        boolean[] buf = paths.get(0).clone();
        printArr(buf);
        boolean tmp;
        while(!checkTerminate(paths)) {
            while(paths.contains(buf)) {
                tmp = buf[buf.length - 1];
                for(int i = buf.length - 1; buf[i - 1] != tmp && 0 < i; i--) {
                    buf[i] = !buf[i];
                    for(int j = 0; checkValid(buf) && j < i; j++)
                        buf[j] = !buf[j];
                }
            }
            paths.add(buf.clone());
            printArr(buf);
        }
        System.out.println(paths.size());
    }

    public static boolean checkTerminate(ArrayList<boolean[]> paths) {
        boolean[] endPath = new boolean[GRIDSIZE * 2];
        for(int i = 0; i < GRIDSIZE; i++) {
            endPath[i] = false;
            endPath[GRIDSIZE * 2 - 1 - i] = true;
        }
        return paths.contains(endPath);
    }

    public static boolean checkValid(boolean[] arr) {
        int countR = 0,
            countL = 0;
        for(int i = 0; i < arr.length; i++) 
            if(arr[i])
                countR++;
            else
                countL++;

        return countR == countL;
    }

    public static void printArr(boolean[] arr) {
        for(int i = 0; i < arr.length; i++)
            System.out.print(arr[i] ? "right " : "down ");
        System.out.println();
    }
}

它在某种程度上不会改变任何地方。

right right down down 
right right down down 
right right down down 
right right down down ...

等等就是它的输出。似乎代码根本不会排列我的路径,但也不会卡在任何for循环中。我最好的猜测是我的功能标准放在错误的序列中

我也想过两年前在学校里像迷宫一样的回溯解决方案,但是我想在重做之前看看这种方法是否可行。

修改
我将尝试尽快实现2 x 2网格示例的图像,但ProjectEuler网站目前仍处于维护状态。

2 个答案:

答案 0 :(得分:3)

解决方案由我们可以拥有的“向下”和“向右”运动的组合数量给出。由于没有回溯,因此总共N向下和N向右移动(在任何路线中,对于NxN网格)。总共2N次移动。

我们可以使用二项式系数 n C r (发音为“n choose r”)来获得这个,这是从r个对象中选择n个对象的方法(每个对象可以是两个东西)。在我们的例子中,“物体”是向下或向右的运动。这是由

给出的

enter image description here

因此,我们想要的数字是:

enter image description here

对于N = 2,这会给6。对于N = 20,这会137846528820

答案 1 :(得分:2)

让右边的一个步骤称为R,将一个步骤称为D。

  

为了在n行和m列网格上从左上角到右下角,你必须向右移动m次并向下移动n次。

基本上, 你必须得到m R&n和D的所有可能的安排。

示例:对于2乘2的网格,单词RRDD的唯一排列数将是您可以去的方式的数量,即

  • RRDD
  • RDRD
  • DRDR
  • 复员

谷歌计算重复字母排列的公式,由下式给出:

  

N! /(r 1 !* r 2 !...),其中所有r的总和为n。

在查找重复的字母排列计数时,Math SE上的

This question会首先出现,second answer在我看来更好地解释了。

因此,要返回计数甚至返回路径,您根本不需要遍历迷宫。只需对第一个进行公式计算,然后打印第二个问题的排列。

当某些步骤偏离网格时给出路径将是唯一需要您实际遍历迷宫的情况。

<强>更新

有助于可视化重复字母排列的公式。

这是一张演示此案例的幻灯片。看看2 E在生成排列时最终是如何重复排列的。一般来说,任何重复 r 次的字母都会导致 r !重复,因为在放置信件的任何地方,可以用另一个相同的字母替换而不给出新的排列。

这样,如果我们除以总计 n !使用 r !进行排列,我们得到实际的唯一排列。

Repeated letter permutations

<子> Image Source