【发布时间】:2021-08-17 10:52:20
【问题描述】:
编辑 2
根据 Gavin 的建议,我将 offset 代码分离成一个新方法:
private static int getOffset(int offset, int row, int col, ArrayList<ArrayList<Integer>> triangle, ArrayList<ArrayList<Integer>> p_triangle, ArrayList<Integer> sums) {
int row_num = (row+1); //= 1-indexed row #
int p_value = p_triangle.get(row).get(col); // number from pascal's triangle
if (col > 1) {
// element is in the left half of Pascal's Triangle
if (col <= (row_num/2)) offset++;
// penultimate element
else if (col == row_num - 2) offset = sums.size() - p_value;
// elements halfway until penultimate;
// [-2, -3] all work up until row 10 and fail thereafter
else offset = sums.size() - p_value - (row_num - col - 2);
}
return offset;
}
并且发现,奇怪的是,在计算给定行后半部分(在中途和倒数第二之间)的元素的偏移量时,减去 2 或 3 都有效。我不知道为什么会这样。
更奇怪的是我修改了奥列格的答案
public static int findMaxSum(ArrayList<ArrayList<Integer>> data) {
for (int row = data.size() - 2; row >= 0; row--)
for (int col = 0; col < data.get(row).size(); col++)
data.get(row).set(col, data.get(row).get(col) + Math.max(data.get(row + 1).get(col), data.get(row + 1).get(col + 1)));
return data.get(0).get(0);
}
并发现算法的行为在大小为 10 的三角形内似乎是正确的。但是,此后它开始崩溃,在第 11-15 行出现以下差异:
size = 11 [correct:772 | mine:752]
size = 12 [correct:850 | mine:830]
size = 13 [correct:908 | mine:921]
size = 14 [correct:981 | mine:961]
size = 15 [correct:1074 | mine:1059]
很遗憾,我仍然无法从中辨别出其中的规律。
编辑 我想强调一下,我并不是在寻找更好的方法来解决这个特定的 Project Euler 问题。相反,我只想知道是否可以按照我描述的方式(或以一些稍微修改的方式)使用 Pascal 的三角形来完成它,如果有人可以看到我的代码中的逻辑,我可能会视而不见。
原始问题 我正在尝试解决Project Euler problem 18。
目标是找到沿数字三角形的所有 2^14 条路径的最大总和。
我对与帕斯卡三角形的相似性感到震惊,想知道它是否可以用来解决问题。
我的逻辑如下:
1.) 按行计算总和。 2.) 使用帕斯卡三角形来确定必须有多少(因为每行加起来是 2 的幂)并确定从前几行总和开始的偏移量。
例如
Pascal's Triangle
1
1 1
1 2 1
1 3 3 1
Triangle To Process
3
7 4
2 4 6
8 5 9 3
Sums
[3]
[10, 7]
[12, 14, 11, 13]
[20, 17, 19, 16, 23, 20, 22, 16]
对于第 3 行,我们看到帕斯卡三角告诉我们将有 1 + 2 + 1 或 4 个值。此外,它还描述了如何构建总和,因为它是直接添加到它们之前的总和中的第一个和最后一个元素,以及添加到这两个总和的中间值,因为它与前面的两个链都有联系。
通过扩展,第四行表明要处理的三角形中的第二个数字应该添加到第三行的前三个总和中,第三个数字应该添加到最后三个总和中。
我得到偏移量的方式有点丑(也许是麻烦的根源):
if (i > 1) {
if (i < (p_triangle.get(row).size()/2)) offset++;
else if (i == triangle.get(row).size()-2) offset = sums.size() - p_triangle.get(row).get(i);
else offset = sums.size() - p_triangle.get(row).get(i) - (p_triangle.get(row).size() - i - 2);
}
其中p_triangle.get(row) 是当前使用的帕斯卡三角行,sums 是累积和数组(长度为 2^(row-1)),偏移量是从哪里开始求和,帕斯卡三角number 是求和列表中从偏移量开始的元素的数量,以求和要处理的三角形中索引 i 处的数字,即 triangle.get(row).get(i)。
我知道这可能不是解决问题的最有效算法,但它似乎是一个不错的算法。问题是,我无法让它工作。
问题答案的剧透警告 正确答案显然是 1074
谁能告诉我使用帕斯卡三角形的代码或逻辑中的什么地方,我可能搞砸了?
完整代码:
import java.util.Scanner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.lang.Math;
public class MaxPathSum {
private static ArrayList<ArrayList<Integer>> pascalsTriangle(int n_rows) {
ArrayList<ArrayList<Integer>> triangle = new ArrayList<>();
triangle.add(new ArrayList<Integer>(){{add(1);}});
triangle.add(new ArrayList<Integer>(){{add(1); add(1);}});
for (int row = 2; row < n_rows; row++) {
ArrayList<Integer> next_row = new ArrayList<>();
next_row.add(1);
for (int i = 1; i < triangle.get(row-1).size(); i++) {
next_row.add(triangle.get(row-1).get(i-1) + triangle.get(row-1).get(i));
}
next_row.add(1);
triangle.add(next_row);
}
return triangle;
}
private static ArrayList<ArrayList<Integer>> buildTriangle(int n_rows) {
Scanner sc = new Scanner(System.in);
ArrayList<ArrayList<Integer>> triangle = new ArrayList<>();
for (int row = 1; row <= n_rows; row++) {
ArrayList<Integer> row_arr = new ArrayList<>();
for (int elem = 1; elem <= row; elem++) {
row_arr.add(sc.nextInt());
}
triangle.add(row_arr);
}
return triangle;
}
private static int findLargestSum(ArrayList<ArrayList<Integer>> triangle, ArrayList<ArrayList<Integer>> p_triangle) {
ArrayList<Integer> sums = new ArrayList<>();
sums.add(triangle.get(0).get(0));
// traverse the rows
for (int row = 1, offset = 0; row < triangle.size(); row++, offset = 0) {
ArrayList<Integer> new_sums = new ArrayList<>();
// traverse each element in each row
new_sums.add(sums.get(0) + triangle.get(row).get(0));
for (int i = 1; i < triangle.get(row).size()-1; i++) {
int n_times = p_triangle.get(row).get(i);
for (int j = 0; j < n_times; j++) {
new_sums.add(triangle.get(row).get(i) + sums.get(j+offset));
}
if (i > 1) {
if (i < (p_triangle.get(row).size()/2)) offset++;
else if (i == triangle.get(row).size()-2) offset = sums.size() - p_triangle.get(row).get(i);
else offset = sums.size() - p_triangle.get(row).get(i) - (p_triangle.get(row).size() - i - 2);
System.out.println("Row: " + row + " | Offset: " + offset);
}
}
new_sums.add(sums.get(sums.size()-1) + triangle.get(row).get(triangle.get(row).size()-1));
sums = new_sums;
}
Collections.sort(sums);
return sums.get(sums.size() - 1);
}
public static void main(String[] args) {
int n_rows = Integer.parseInt(args[0]);
// build pascalsTriangle
ArrayList<ArrayList<Integer>> p_triangle = pascalsTriangle(n_rows);
// build triangle from input
ArrayList<ArrayList<Integer>> triangle = buildTriangle(n_rows);
// traverse triangle finding largest sum
int largest_sum = findLargestSum(triangle, p_triangle);
// display results
System.out.println(largest_sum);
}
}
【问题讨论】:
-
如果您怀疑获取偏移量的代码是您的问题,也许如果您稍微更改代码以便可以测试代码,单独确定偏移量可能会有所帮助。这可以通过将其放入返回偏移量并测试该方法的方法中来完成。
-
这是个好主意。我删除了一堆日志记录
System.out.println(),试图让代码不那么混乱。我确实打印了偏移量,据我所知(至少在较低的行),这对我来说似乎是正确的。这就是为什么我想知道我的逻辑是否错误。 -
我认为在这种情况下,测试会比试图理解日志输出提供更好的结果。另一种可能性是调试,如果偏移代码在其自己的方法中,这可能会更容易。
-
@Gavin 我会按照你说的尝试将它分离成自己的功能,看看我能从那里做些什么。