// CS 306 - Computer Algorithms II
// Fall 2007
//
// A brute-force solution to the max-sum triangle path problem.
#include <vector>
#include <cassert>
#include <iostream>
typedef std::pair<unsigned, unsigned> coordinate
typedef std::vector<coordinate> path
typedef std::vector<path> path_set
template <typename T>
class triangle_matrix
// A minimum-space impelementation of a lower-left triangular matrix.
public:
// Return the value stored at the given coordinate in this triangle.
const T
get(unsigned row, unsigned col) const
return data.at(index(row, col))
// Store the given value at the given coordinate in this triangle.
void
put(unsigned row, unsigned col, T val)
data.at(index(row, col)) = val
// Return the number of rows in this triangle.
unsigned
row_count() const
return rows
// Create a new triangle with the given number of rows. The contents of
// the triangle are undefined.
explicit triangle_matrix(unsigned rows)
: rows(rows), data(gauss(rows))
private:
unsigned rows
std::vector<T> data
unsigned
gauss(unsigned n) const
return (n*(n + 1))/2
unsigned
index(unsigned r, unsigned c) const
assert ((r < data.size()) and (c <= r))
return gauss(r) + c
class number_triangle
// An equilateral triangle full of random integers.
public:
// Return the number at the given coordinates in this triangle.
const int
get(const coordinate & c) const
return data.get(c.first, c.second)
// Create a triangle with the given number of rows; the triangle contains
// random integers.
explicit
number_triangle(unsigned size)
: data(size)
const unsigned N = 100
srandom(getpid())
for unsigned r = 0; r < size; r++
for unsigned c = 0; c <= r; c++
data.put(r, c, (random() % N) - N/2)
// Return the number of rows in this triangle.
unsigned
row_count() const
return data.row_count()
private:
triangle_matrix<int> data
// Deja vu c++ style.
static path_set path_move(unsigned, unsigned, unsigned, unsigned)
static int path_sum(const number_triangle &, const path &)
static path max_sum_path(const number_triangle &)
static path max_sum_path_dp(const number_triangle &)
static std::ostream & operator << (std::ostream &, const coordinate &)
static std::ostream & operator << (std::ostream &, const path &)
static path_set
all_paths(unsigned row, unsigned col, unsigned N)
// Return all possible paths through a sub-triangle with its apex at the
// given coordinates in a triangle with the given number of rows.
path_set paths
if row + 1 == N
paths.push_back(path())
paths.back().push_back(coordinate(row, col))
else
const path_set right_paths = path_move(row, col, 1, N)
paths = path_move(row, col, 0, N)
std::copy(
right_paths.begin(), right_paths.end(), std::back_inserter(paths))
return paths
static path_set
all_paths(const number_triangle & t)
// Return all possible paths from the apex to the base in the given triangle.
return all_paths(0, 0, t.row_count())
int
main(int argc, char * argv[])
if argc == 1
std::cerr << "Missing command-line argument.\n"
<< "Command-line format is \"triangle-size\".\n"
exit(EXIT_FAILURE)
const number_triangle t(atoi(argv[1]))
path msp = max_sum_path(t)
std::cout << "The max-sum value is " << path_sum(t, msp) << "\n"
std::cout << "The max-sum path is " << msp << "\n"
exit(EXIT_SUCCESS)
static path
max_sum_path(const number_triangle & t)
// Return a path through the given triangle that has maximum sum.
path max_path
const path_set paths = all_paths(t)
if not paths.empty()
max_path = paths.front()
int max_sum = path_sum(t, max_path)
for unsigned i = paths.size() - 1; i > 0; i--
const path & p = paths[i]
const int sum = path_sum(t, p)
if sum > max_sum
max_sum = sum
max_path = p
return max_path
static path
max_sum_path_dp(const number_triangle & t)
// Return a path through the given triangle that has maximum sum.
const unsigned N = t.row_count()
if N == 0
return path()
typedef std::pair<int, path> partial_path
// table(r, c) is the partial path through the (sub-)triangle with apex
// (r,c).
triangle_matrix<partial_path> table(N)
// Fill in the bottom row of the table.
for unsigned i = 0; i < N; i++
const coordinate apex(N - 1, i)
partial_path pp(t.get(apex), path())
pp.second.insert(pp.second.begin(), apex)
table.put(N - 1, i, pp)
for unsigned row = N - 1; row > 0;
const unsigned previous_row = row--
for unsigned i = 0; i <= row; i++
const coordinate apex(row, i)
// Set the current apex's partial path to the larger of the partial paths
// down to the left and down to the right of the current apex.
partial_path
left = table.get(previous_row, i),
right = table.get(previous_row, i + 1),
& choice = left.first > right.first ? left : right,
next(choice.first + t.get(apex), choice.second)
// Add the current apex to the new partial path and store the path in the
// table.
next.second.insert(next.second.begin(), apex)
table.put(row, i, next)
return table.get(0, 0).second
static std::ostream &
operator << (std::ostream & os, const coordinate & c)
// Write the given coordinate to the given output stream; return the stream.
return os << "(" << c.first << " " << c.second << ")"
static std::ostream &
operator << (std::ostream & os, const path & p)
// Write the given path to the given output stream; return the stream.
os << "["
// What I want to write next is
//
// std::copy(p.begin(), p.end(), std::ostream_iterator<coordinate>(os))
//
// but debian testing g++ 4.1.3 won't compile that for some reason.
char * sep = ""
for unsigned i = 0; i < p.size(); i++
os << sep << p[i]
sep = " "
return os << "]"
static path_set
path_move(unsigned row, unsigned col, unsigned move, unsigned N)
// Return all the paths that start at sub-triangle apex with the given
// coordinates and make the given move from the apex.
path_set paths = all_paths(row + 1, col + move, N)
const coordinate apex(row, col)
for unsigned i = paths.size(); i > 0;
path & p = paths[--i]
p.insert(p.begin(), apex)
return paths
static int
path_sum(const number_triangle & t, const path & p)
// Return the path sum for the given path through the given triangle.
int sum = 0
for unsigned i = p.size(); i > 0;
sum += t.get(p[--i])
return sum
// $Log: number-triangle.cc,v $
// Revision 1.3 2007-09-23 15:20:00 rclayton
// Remove some unneeded includes.
//
// Revision 1.2 2007-09-16 22:19:26 rclayton
// Document.
//
// Revision 1.1 2007-09-10 23:04:07 rclayton
// Created.
//
syntax highlighted by Code2HTML, v. 0.9.1