问题在于:https://projecteuler.net/problem=15
我想出了一个我认为可以为此工作的模式,我已经看过其他人做了什么,他们做了同样的事情,比如这里:{{ 3}}但我总是得到一个不同的答案。这是我的代码。
import java.util.*;
public class Problem15 {
public static void main(String[] args) {
ArrayList<Long> list = new ArrayList<Long>();
list.add((long)1);
int size;
for (int x = 1;x<20;x++){
size = list.size();
for(int y = 1;y<size;y++){
long sum = list.get(y-1)+list.get(y);
list.set(y, sum);
}
list.add(list.get(size-1)*2);
System.out.println(list);
}
}
}
编辑:
为了回应爱德华,我认为我的方法是你在编辑之前所说的,因为这不是关于蛮力的,但我只是从网格中的每个点总结可能的方法。但是,我不需要一个二维阵列来做这个,因为我只是从侧面看可能的移动。这是我编写的内容,希望能够解释我的过程。
所以对于1x1。就像你说的那样,一旦达到一个方向的极限,你只能在另一个方向的极限上旅行,所以有两种方式。这对于1x1并不是特别有用,但对于较大的1x1则是如此。对于2x2,您知道作为右边界限的顶角只有1个可能的路径。同样的逻辑适用于底角。并且,因为你有一个已经解决过的方形,1x1,你知道中间点有2条路径。现在,如果你看两边,你会看到例如在它下面有2而在右边有1的点有这些相邻点中的路径数的总和,所以那个点必须有3个路径。对于另一方也是如此,左上角的总和为3和3,或者是2倍。
现在,如果您查看我的代码,那就是它正在尝试做的事情。索引为0的元素始终为1,对于数组的其余部分,它将前一个术语与其自身相加,并替换当前术语。最后,为了找到路径总数,它只是最后一个数字的两倍。因此,如果程序尝试解决4x4,则该阵列目前看起来像{1,4,10,20}。因此程序会将其更改为{1,5,10,20},然后{1,5,15,20},然后{1,5,15,35},最后添加路径总数{{ 1,5,15,35,70}。我认为这是你在答案中向我解释的内容,但我的答案总是错误的。
答案 0 :(得分:5)
意识到数学复杂性比蛮力搜索更重要。
您有一个二维点阵列,您可以选择仅在x或y方向远离原点。因此,您可以像这样代表您的旅行:
(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)
有些事情变得很明显。第一个是通过混乱的任何路径将需要x + y步,穿过x + y + 1个位置。这是曼哈顿距离风格路径的一个特征。
第二个是在任何一个点,直到你达到最大x或y,你可以选择两个选项之一(x或y);但是,只要一个或另一个达到它的极限,剩下的唯一选择是重复选择非最大值,直到它也变为最大值。
有了这个,你可能有足够的提示来解决数学问题。然后,您甚至不需要搜索不同的路径来获得可以解决问题的算法。
---编辑以提供更多提示---
每个二维路径数组可以分解为更小的二维路径数组。因此,函数f(3, 5)
产生路径数的f
解决方案等于f(2, 5) + f(3, 4)
。请注意,f(0, 5)
直接等于1
,f(3, 0)
也是如此,因为当路径被强制为线性时,您不再有“选择”。
对函数建模后,您甚至不需要数组来遍历路径......
f(1, 1) = f(0, 1) + f(1, 0)
f(0, 1) = 1
f(1, 0) = 1
f(1, 1) = 1 + 1
f(1, 1) = 2
和一组3 x 3顶点(如引用的例子)
f(2, 2) = f(1, 2) + f(2, 1)
f(1, 2) = f(0, 1) + f(1, 1)
(从之前)
f(1, 1) = 2
f(0, 2) = 1
f(1, 2) = 2 + 1 = 3
同样(因为它是镜像)
f(2, 1) = 1 + 2 = 3
所以
f(2, 2) = 3 + 3 = 6
---最后编辑(我希望!)---
好的,现在你可能会认为你有两个选择(向下)或(向右)。考虑一个包含四个“命令”的包,2个“向下”和2个“向右”。您可以从包中选择多少种不同的方式?
这样的“选择”是一种排列,但由于我们选择了所有这些,它是一种特殊的排列类型,称为“顺序”或“排序”。
二项式(一个或另一个)排序的数量由数学公式
决定订单数量= (A + B)!/(A! * B!)
其中A
是A
类型的项目的“计数”,而B
是B
类型的项目的“计数”
3x3顶点,2个向下选择,2个正确选择
订购数量=(2 + 2)!/ 2!* 2!
4!/1*2*1*2
1*2*3*4/1*2*1*2
(1*2)*3*4/(1*2)*1*2
3*4/2
12/2
6
如果需要的话,你可以手动做20 * 20,但是阶乘公式很容易通过计算机完成(虽然请注意你不要用整数溢出破坏答案)。
答案 1 :(得分:0)
另一种实施方式:
public static void main(String[] args) {
int n = 20;
long matrix[][] = new long[n][n];
for (int i = 0; i < n; i++) {
matrix[i][0] = i + 2;
matrix[0][i] = i + 2;
}
for (int i = 1; i < n; i++) {
for (int j = i; j < n; j++) { // j>=i
matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1];
matrix[j][i] = matrix[i][j]; // avoids double computation (difference)
}
}
System.out.println(matrix[n - 1][n - 1]);
}
时间:43微秒(不打印)
它基于以下矩阵:
| 1 2 3 4 ...
---------------------
1 | 2 3 4 5 ...
2 | 3 6 10 15
3 | 4 10 20 35
4 | 5 15 35 70
. | .
. | .
. | .
,其中
6 = 3 + 3
10 = 6 + 4
15 = 10 + 5
...
70 = 35 + 35
请注意,我在实现中使用了i + 2
而不是i + 1
,因为第一个索引是0
。
当然,最快的解决方案是使用数学公式(参见Edwin的帖子)及其代码:
public static void main(String[] args) {
int n = 20;
long result = 1;
for ( int i = 1 ; i <= n ; i++ ) {
result *= (i+n);
result /= i;
}
System.out.println(result);
}
只需 5微秒(不打印)。
如果您担心精度会下降,请注意n
个连续数字的乘积可以被n!
整除。
要更好地理解为什么公式是:
(d+r)!
F = --------- , where |D| = d and |R| = r
d!*r!
而不是F = (d+r)!
,假设每个“向下”和“向右”都有一个索引:
down1,right1,right2,down2,down3,right3
第二个公式计算上面“命令”的所有可能排列,但在我们的例子中,down1,down2和down3之间没有区别。因此,第二个公式会将6
(3!
)计为同一个数字:
down1,down2,down3
down1,down3,down2
down2,down1,down3
down2,down3,down1
down3,down1,down2
down3,down2,down1
这就是我们将(d+r)!
除以d!
的原因。类似于r!
。