最大求和路径三角形,并获取添加到C#中的数字

时间:2019-11-20 22:18:29

标签: c#

我正在尝试从三角形的顶部到底部获得最大的总和 输入应该是这样

                      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天,但是我失败了,感谢您的帮助。谢谢

3 个答案:

答案 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>();
   }