我正在尝试从三角形的顶部到底部获得最大的总和 输入应该是这样
5
94 48
95 30 96
77 71 26 67
97 13 76 38 45
07 36 79 16 37 68
48 07 09 18 70 26 06
18 72 79 46 59 79 29 90
20 76 87 11 32 07 07 49 18
27 83 58 35 71 11 25 57 29 85
14 64 36 96 27 11 58 56 92 18 55
02 90 03 60 48 49 41 46 33 36 47 23
92 50 48 02 36 59 42 79 72 20 82 77 42
56 78 38 80 39 75 02 71 66 66 01 03 55 72
44 25 67 84 71 67 11 61 40 57 58 89 40 56 36
85 32 25 85 57 48 84 35 47 62 17 01 01 99 89 52
06 71 28 75 94 48 37 10 23 51 06 48 53 18 74 98 15
27 02 92 23 08 71 76 84 15 52 92 63 81 10 44 10 69 93
输出应为:
Maximum total: 1320
Path (for example) : 55 - 48 -30 - etc
路径应该是我加在一起以获得结果1320的数字。 能够找到总和(1320),但是我找不到一种方法来找到所有汇总的数字。我的代码不仅返回所有数字,还返回所有数字。
static void Sum()
{
int[,] list = new int[18, 19];
int[,] list2 = new int[18, 19];
string input = @"55
94 48
95 30 96
77 71 26 67
97 13 76 38 45
07 36 79 16 37 68
48 07 09 18 70 26 06
18 72 79 46 59 79 29 90
20 76 87 11 32 07 07 49 18
27 83 58 35 71 11 25 57 29 85
14 64 36 96 27 11 58 56 92 18 55
02 90 03 60 48 49 41 46 33 36 47 23
92 50 48 02 36 59 42 79 72 20 82 77 42
56 78 38 80 39 75 02 71 66 66 01 03 55 72
44 25 67 84 71 67 11 61 40 57 58 89 40 56 36
85 32 25 85 57 48 84 35 47 62 17 01 01 99 89 52
06 71 28 75 94 48 37 10 23 51 06 48 53 18 74 98 15
27 02 92 23 08 71 76 84 15 52 92 63 81 10 44 10 69 93";
var charArray = input.Split('\n');
for (int i = 0; i < charArray.Length; i++)
{
var numArr = charArray[i].Trim().Split(' ');
for (int j = 0; j < numArr.Length; j++)
{
int number = Convert.ToInt32(numArr[j]);
list[i, j] = number;
list2[i, j] = number;
}
}
StringBuilder path = new StringBuilder();
for (int i = 16; i >= 0; i--)
{
for (int j = 0; j < 18; j++)
{
//list[i, j] = Math.Max(list[i, j] + list[i + 1, j], list[i, j] + list[i + 1, j + 1]);
if (list[i, j] + list[i + 1, j] > list[i, j] + list[i + 1, j + 1])
{
if (list2[i, j] > 0)
path.Append(list2[i, j] + " - ");
if (list2[i+1, j] >0)
path.Append(list2[i+1, j] + " - ");
list[i, j] = list[i, j] + list[i + 1, j];
}
else
{
//path.Append(list2[i, j] + " - " + list2[i + 1, j + 1]+ " - ");
if (list2[i, j] > 0)
path.Append(list2[i, j] + " - ");
if (list2[i + 1, j] > 0)
path.Append(list2[i + 1, j] + " - ");
list[i, j] = list[i, j] + list[i + 1, j + 1];
}
}
}
Console.WriteLine(string.Format("Maximum total: {0} \n Path {1}", list[0, 0], path));
我已经尝试了4天来解决它4天,但是我失败了,感谢您的帮助。谢谢
答案 0 :(得分:1)
鉴于问题的家庭作业性质,我不愿彻底解决该问题。这样做的任何答案都会给您和您的老师带来伤害。
也就是说,假设您了解https://www.mathblog.dk/project-euler-18/中介绍的动态编程方法,那么我希望无需真正编写即可提供有关您的解决方案外观的一些见解会有所帮助。 em>为您提供的解决方案。
因此,特别地,让我们考虑所描述的算法。该算法的本质在于,它可以解决三角形底部的所有子问题,将结果从底部的一行折叠(或“折叠”)到上面的下一行。
关键是要注意,完成此操作后,对于更新后的上一行中的每个值,将通过在紧挨下一行的左侧或右侧添加一个或另一个值来进行更新。如果您希望能够显示最大解通过三角形的路径,则只需记住每行计算新最大值的方向。
这很容易通过引入一个具有与原始尺寸相同尺寸的新数组来完成,但是要在其中代替输入值,在数组中存储一些有关该值是否是通过从在下面的行中向左或向右(自然地,该数组在最底部的行中不需要任何实际数据…您可以只忽略那里的值,或者可以使该数组比原始数据的数组小一行) )。
然后,一旦完成算法并到达数值数组的顶部,您还将拥有这种“面包屑”数组,告诉您如何到达那里。从只有一个元素的第一行开始,您可以查看面包屑数组,以回忆下一行中的哪个元素已添加到该元素。
有了这些信息,您现在知道使用了第二行中的哪个元素。面包屑数组告诉您使用了 (第三行)下面一行的哪个元素。依此类推。
还有其他方法可以设计数据结构以实现相同的目标。但是,它们全都归结为相同的基本思想:将有关每个总和的计算信息存储在某个位置,以便以后可以重现。
您应该不难修改已存在的代码,包括此附加数组,然后在以后使用它来调用已采用的路径。
最后,本着动态编程方面的精神,我将指出,如果在进行过程中构建输出字符串,则甚至不需要多维数组。取而代之的是,您仅从保存字符串的数组开始,该字符串与倒数第二行一样长,并且每个数组元素中存储的第一个字符串就是添加到倒数第二个元素中的当前元素的编号-最后一行。
在每个新行中,您将在前一行(用于当前总和)的前面加上原始号来替换每个字符串,即在开头插入。您需要跟踪原始编号-这很容易,因为您已经在代码中维护了该数组的两个副本。
我将注意到,这种“仅保留一维数组”技术也可以用于运行总计。即您真正需要的唯一二维数组是具有原始数据的数组。在每次迭代中,您只关心最近的行,因此该行只需要一个一维数组。
答案 1 :(得分:1)
“ ...但是我找不到找到所有汇总数字的方法”
您如此很亲密。您维护着所有必要的信息,以便能够构建通过金字塔的路径。嵌套的for循环后,list
看起来像这样。您能想到一种简单的遍历list
来查找路径的方法吗?
1320
1265 1130
1171 1061 1082
1076 1031 986 925
999 903 960 813 858
902 890 884 754 775 813
895 854 805 682 738 694 745
793 847 796 664 644 668 660 739
719 775 717 618 585 543 589 631 649
643 699 630 607 553 522 536 582 554 631
566 616 512 572 482 466 511 509 525 509 546
489 552 465 476 454 455 448 453 433 384 491 467
487 445 462 416 383 406 364 407 400 348 342 444 426
348 395 372 414 332 347 253 322 328 328 225 260 367 384
292 220 317 334 293 272 216 251 245 262 204 224 257 312 292
248 195 145 250 222 172 205 129 190 205 146 135 135 217 256 219
33 163 120 98 165 124 121 94 75 143 98 129 134 62 118 167 108
27 2 92 23 8 71 76 84 15 52 92 63 81 10 44 10 69 93
答案 2 :(得分:0)
动态编程的另一种方法是执行depth first search,因为金字塔只是一棵树。这对应于引用的@peterduniho博客中的“蛮力”技术。基本思想非常简单:检查从根(55)到叶节点(27、2等)的每条路径。
我设置了脚手架以助您前进。您可能会发现使用这种数据表示形式更容易一些,因为您不必过多地考虑索引和一次性错误,而您可以将更多精力放在解决问题上。它还使使用Visual Studio的调试功能更加容易(例如,可以在代码中放置一个断点,并查看后代和后代的后代,等等。)
如果您不熟悉递归,则代码中需要注意的关键是DepthFirstSearch
会自行调用。
(我认为动态编程是解决此问题的更好方法,但我只是将其发布为替代方法)
public void Solve()
{
string input =
@"55
94 48
95 30 96
77 71 26 67
97 13 76 38 45
07 36 79 16 37 68
48 07 09 18 70 26 06
18 72 79 46 59 79 29 90
20 76 87 11 32 07 07 49 18
27 83 58 35 71 11 25 57 29 85
14 64 36 96 27 11 58 56 92 18 55
02 90 03 60 48 49 41 46 33 36 47 23
92 50 48 02 36 59 42 79 72 20 82 77 42
56 78 38 80 39 75 02 71 66 66 01 03 55 72
44 25 67 84 71 67 11 61 40 57 58 89 40 56 36
85 32 25 85 57 48 84 35 47 62 17 01 01 99 89 52
06 71 28 75 94 48 37 10 23 51 06 48 53 18 74 98 15
27 02 92 23 08 71 76 84 15 52 92 63 81 10 44 10 69 93";
var tree = input.Split('\n')
.Select(line => line.Trim()
.Split(' ')
.Select(s => new Node(int.Parse(s.Trim())))
.ToArray())
.ToArray();
for (var i = 0; i < tree.Length-1; i++)
{
for (var j = 0; j < tree[i].Length; j++)
{
tree[i][j].Descendants.Add(tree[i + 1][j]);
tree[i][j].Descendants.Add(tree[i + 1][j+1]);
}
}
var root = tree[0][0];
int maxSum = 0;
DepthFirstSearch(tree[0][0], ImmutableList<Node>.Empty.Add(root), 0, ref maxSum);
}
private void DepthFirstSearch(Node root, ImmutableList<Node> path, int runningSum, ref int maxSum)
{
CheckBaseCase(root, path, ref maxSum);
foreach(var node in root.Descendants)
{
runningSum += node.Value;
DepthFirstSearch(node, path.Add(node), runningSum, ref maxSum);
}
}
private void CheckBaseCase(Node root, ImmutableList<Node> path, ref int maxSum)
{
// Fill me in!
}
[DebuggerDisplay("Value = {Value}, DescendantCount={Descendants.Count}")]
class Node
{
public Node(int value)
{
Value = value;
}
public int Value{ get; }
public List<Node> Descendants { get; } = new List<Node>();
}