// CS 306 - Computer Algorithms II
// Fall 2008
//
// 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