欧拉项目#18方法

时间:2011-11-03 21:40:30

标签: algorithm

我正在研究欧拉项目。特别是#18。
总而言之,我们的想法是从三角形中找到最大路径:

   3
  7 4
 2 4 6  
8 5 9 3

3 + 7 + 4 + 9 = 23.

对此进行阅读,大多数人表示这是通过自下而上的工作而不是使用从上到下工作“贪婪”的算法来正确解决的。

我可以理解,从顶部开始向下选择你发现的最大值是“短视”,可能不是整体最大值。

但为什么从底部到顶部的方法更好呢? 在我看来,它遇到了同样的问题。

例如,在示例中的三角形中我们将得到(从底部开始):
9 + 6 + 4 + 3 = 22 < 23

那么为什么要从下到上开始呢?

9 个答案:

答案 0 :(得分:123)

这就是所谓的动态编程。

你有这样的三角形:

   3
  7 4
 2 4 6  
8 5 9 3

当您从底部移动到顶部时,您可以计算最后一次迭代的最佳选择。 在这种情况下,您将获取最后一行8 5 9 3并最大化除前一行之外的总和。

迭代1: 假设你在last-1步。

你有一行2 4 6,让我们迭代它。

从2 ,你可以去 8 或5,所以 8 更好(从2开始最大化你的结果)所以你计算第一笔8 + 2 = 10。

从4 ,您可以转到5或 9 ,因此 9 更好(从4开始最大化您的结果),因此您需要计算第二笔9 + 4 = 13。

从6 ,你可以去 9 或3,所以 9 再次更好(从6开始最大化你的结果)所以你计算第三笔9 + 6 = 15。

这是第一次迭代的结束,你得到了总和10 13 15

现在你有了较低维度的三角形:

    3
  7  4
10 13 15  

现在继续,直到你得到一个值,正好是23。

答案 1 :(得分:37)

差异不在自上而下和自下而上。区别在于贪婪和“前沿”方法。

贪婪的算法不一定会帮助你,因为如果树的最佳部分无法触及,你将无法恢复。例如:贪婪算法将从上到下选择路径1-3。它会完全错过9。

    1
   2 3
  9 1 1

为了找到真正的最大值,你必须基本上遍历所有路径。

如上所述,自下而上的方法没有这个问题。它最多检查n *(n-1)个路径:每个元素2个。然而,称之为“自下而上”的方法具有误导性。

为什么呢?因为有一种自上而下的方法是等价的。其实质是你有一种“边疆”,对边境后面的所有树木都有最好的结果。无论您是向上还是向下移动边界都是次要的。对于上例中的自上而下方法,您可以为每一行计算每个元素的总和以及它上面两个最佳总数的最大值:

     1
    3 4
  12 5 5

在自下而上的方法中,您为每一行计算每个元素的总和以及它下面两个最佳总数的最大值。以相反的顺序:

  9  1 1
   11 4
    12

这些自上而下和自下而上的方法都有相同的工作。

答案 2 :(得分:10)

使用您的示例,接近它的“自下而上”方式是:

检查底行,你可以从每个元素得到的最多是

  

8,5,9,3

检查从下到下的行,你可以从每个元素得到的最多(取决于你是从左边还是右边):

  

2 + max(8,5),4 + max(5,9),6 + max(9,3)= 10,13,15

所以这很棒;我们通过将它们压在一起来消除2行,将它们替换为一行,将问题减少到

     3
   7   4
10  13  15

显然我们可以继续重复这个。检查下一行,你可以从每个元素得到的最多是

  

7 + max(10,13),4 + max(13,15)= 20,19

从顶部开始,你可以获得的最多是

  

3 + max(20,19)= 23

QED。

答案 3 :(得分:6)

实际上,你不需要自下而上;你可以自上而下开始,只要你做得好。

自下而上的工作方式最好地说明了金字塔每个层面发生的事情。该路径肯定必须在某个时刻穿过每个级别。

    x
   x x
  x h x
 x y y x
x y y y x

我们说它是h。根据允许路径的定义,路径只能跟随到y - 标记的位置,这会形成类似于原始的问题 - 如果我们找到通过y s和最大值的最大路径整个三角形的路径实际上经过h,它肯定会跟随y s中的最大路径(如果没有,你可以在较小的三角形中切换路径的一部分并获得整体更好的路径)。

因此,如果您构建算法自上而下计算从当前节点向下的最大路径,您将获得正确的结果(即最大路径值,您可以从中轻松获取路径本身)。

现在,这需要O(N)(N表示数字的数量),因为对于每个地方,您只考虑两个路径并使用较低级别的预先计算的值。

实际上,自上而下可以实现相同的算法,只要您记住结果,就可以从顶部开始并向下递减。

best_length(node)
{ 
  if(node is terminal)
    return value(node)
  int m = 0
  for(next : lower neighbors of node)
    m = max(best_length(next), m)
  return m + value(node);
}

自上而下这样做的另一种可能性就是反转计算。您从顶部开始,针对考虑其上邻居的每个节点,从该节点的顶部结尾获取路径长度(而不是从该节点向下到底行的路径)。最后,您从最底行收集数据,然后就完成了。

答案 4 :(得分:4)

采用自下而上的方法消除路径,同时仅自上而下添加潜在路径。

因为您可以更快地消除错误路径,所以执行广度优先搜索会成为最佳解决方案。任何一个方向的深度优先搜索都是错误的(正如您所示)并且速度很慢。

答案 5 :(得分:1)

这是一个可以使用图论解决的问题。对于每个点,您只能前往它的两个“孩子”(它下面的左右节点)。对于所有叶节点(底行)包括到“末端节点”的路径(零成本)。

您想要最大的数字,而这又是最长的路径。

为此,您实现了BFS(通常是最短路径算法),但不是将父节点和子节点之间的权重作为子节点的值,而是使其成为子节点的加法逆。子节点值。

你不能在这里轻易使用Dijkstra,因为Dijkstra仅适用于非负路径。

BFS的运行时间为O(| E | + | V |)。
在三角形中有1 + 2 + 3 + 4 + 5 + .. + n =(1/2)(n)(n-1)个节点
这意味着存在(n)(n-1)个路径,以及最终节点连接的(n) 总计:(1/2)(3n ^ 2 -n)其中n是行数。

答案 6 :(得分:1)

由于行数很少,您还可以使用递归计算最大总和:

import sys
def max_sum(i,j):
    if i==14:
        return a[i][j]
    return a[i][j]+max(max_sum(i+1,j),max_sum(i+1,j+1))
a=[]
for i in range(15):
    b=list(map(int,sys.stdin.readline().split()))
    a.append(b)
print(max_sum(0,0))
问题67的

- (最大路径总和II)你可以使用memoisation:

import sys
d={}
def max_sum(i,j):
    if (i,j) in d:
        return d[(i,j)]
    if i==99:
        return a[i][j]
    d[(i,j)]=a[i][j]+max(max_sum(i+1,j),max_sum(i+1,j+1))
    return d[(i,j)]
a=[]
for i in range(100):
    b=list(map(int,sys.stdin.readline().split()))
    a.append(b)
print(max_sum(0,0))

答案 7 :(得分:0)

这是一个完整的答案:

#include <iostream>

using namespace std;

int main()
{
  int sum1,sum2;
  int A[4][4] = {{3,0,0,0},{7,4,0,0},{3,4,6,0},{8,5,9,3}};
  for(int i=2;i>=0;i--){
     for(int j=0;j<4;j++){
        sum1=A[i][j]+A[i+1][j];
        sum2=A[i][j]+A[i+1][j+1];
        if(sum1>sum2){
            A[i][j]=sum1;
        }
        else{
            A[i][j]=sum2;
        }
     }
  }
  cout<<A[0][0]<<endl;
}

答案 8 :(得分:0)

static int getTriangle() throws FileNotFoundException, IOException{
        File file = new File("/home/rakib/This Pc/programming/problem solving/ProjectEuler/src/projecteuler/euluer17.txt");
        BufferedReader br = new BufferedReader(new FileReader(file));
        String n;
        int i = 0;
        int [][] array = new int[15][15];
        while((n = br.readLine()) != null){
            String [] temp = n.split(" ");
            for(int j = 0; j < temp.length; j++){
                 array[i][j] = Integer.parseInt(temp[j]);         
            }
             i++;
        }


    for(int j = array.length-2; j >= 0; j--){
        int []tempArray = new int [j+1];
           for(int k =0; k <= j; k++){
               tempArray[k] = array[j][k] + Math.max(array[j+1][k], array[j+1][k+1]);
           }
           array[j] = tempArray;
    }

 return array[0][0];
}

我使用了自下而上的方法来解决此问题。从最后两行开始。只需在路径元素中添加最大值即可。