In Project Euler\'s problem 67 there is a triangle given and it contains 100 rows. For e.g.
5
9 6
4 6 8
0 7 1 5
I.e. 5 + 9 + 6 + 7
You want to store this as a directed acyclic graph. The nodes are the entries of your triangular array, and there is an arrow from i
to j
iff j
is one row lower and immediately left or right of i
.
Now you want to find the maximum weight directed path (sum of the vertex weights) in this graph. In general, this problem is NP-complete, however, as templatetypedef points out, there are efficient algorithms for DAGs; here's one:
algorithm dag-longest-path is
input:
Directed acyclic graph G
output:
Length of the longest path
length_to = array with |V(G)| elements of type int with default value 0
for each vertex v in topOrder(G) do
for each edge (v, w) in E(G) do
if length_to[w] <= length_to[v] + weight(G,(v,w)) then
length_to[w] = length_to[v] + weight(G, (v,w))
return max(length_to[v] for v in V(G))
To make this work, you will need to weight the length
of the path to be the size of the target node (since all paths include the source, this is fine).
Which language are you using?
A muti-dimensional array is probably the best way to store the values, then depending on the language, there are options for how you store pointers or references to where in the arrays you are.
I believe this is a Project Euler problem.
It can't be represented with a binary tree. Any kind of graph is overkill as well, though the problem can be solved with a longest path algorithm in directed acyclic graphs. Edit: Nevermind that, it is only useful if the edges are weighted, not the nodes.
A two dimensional vector (e.g. vector<vector<int> >
) is more than enough to represent such triangles, and there is a straightforward solution using such representation.
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
int main() {
std::vector<std::vector<int> > lines;
lines.resize(100);
std::ifstream input("triangle.txt");
for (int i = 0; i < 100; i++) {
for (int j = 0; j < i + 1; j++) {
std::string number_string;
input >> number_string;
std::istringstream temp(number_string);
int value = 0;
temp >> value;
lines[i].push_back(value);
}
}
std::vector<int> path1;
path1.resize(100);
std::vector<int> path2;
path2.resize(100);
for (int i = 0;i < 100;i++)
path1[i] = lines[99][i];
for (int i = 98; i >= 0;i--) {
for(int j = 0;j < i+1;j++) {
if(path1[j] > path1[j + 1]){
path2[j] = path1[j] + lines[i][j];
} else{
path2[j] = path1[j + 1] + lines[i][j];
}
}
for (int i = 0;i < 100;i++)
path1[i] = path2[i];
}
std::cout << path1[0] << std::endl;
}
To build off of @Matt Wilson's answer, using a two-dimensional array to hold the numbers would be a fairly simple solution. In your case, you would encode the triangle
5
9 6
4 6 8
0 7 1 5
as the array
[5][ ][ ][ ]
[9][6][ ][ ]
[4][6][8][ ]
[0][7][1][5]
From here, a node at position (i, j) has children at (i + 1, j) and (i + 1, j + 1) and parents at position (i - 1, j) and (i - 1, j - 1), assuming those indices are valid.
The advantage of this approach is that if your triangle has height N, the space required for this approach is N2, which is just less than twice the N(N + 1) / 2 space required to actually store the elements. A linked structure like an explicit graph would certainly use more memory than that.