#include #include using namespace std; /* Consider a bee walking on a honeycomb (hexagonal tiling of the plane). For given n, count the number of n-step paths that end at the same cell from which they started. Each step is from a honeycomb cell to an adjacent cell. For example there are 6 two step paths from a cell back to the same cell and there are 12 3 step paths. */ typedef pair cell; ostream& operator<< (ostream& out, const cell& a) { return out << "[" << a.first << "," << a.second << "]"; } // A cell is denoted by an integer (i, j) point whose sum i + j is even. cell nbr(cell c, int i) // precondition: 0 <= i < 6. // postcondition: returns one of the 6 neighbors of c (in an arbitrary order) { switch (i) { case 0: return cell(c.first + 2, c.second); case 1: return cell(c.first - 2, c.second); case 2: return cell(c.first + 1, c.second + 1); case 3: return cell(c.first + 1, c.second - 1); case 4: return cell(c.first - 1, c.second + 1); case 5: return cell(c.first - 1, c.second - 1); } } inline int abs(int x){ return x < 0 ? -x : x;} bool neighbor (const cell& a, const cell& b) // Precondition: valid cells // Postcondition: returns true iff a and b are adjacent cells (have edge in common). { return a.first != b.first && 2 == abs(a.first - b.first) + abs(a.second - b.second) ; } struct WaysObject /* This class captures "global" data associated with the ways() function, such as the memo table, flags to determine how some aspects like memoization and base cases are handled, a counter to record how many recursive calls get made. */ { int operator()(cell a, cell b, int n) { return ways(a, b, n); } WaysObject(bool m, bool b) : callCount(0), memoize(m), usingBaseCaseOne(b) {} void reset(bool m, bool b) { memoTable.clear(); callCount = 0; memoize = m; usingBaseCaseOne = b; } int ways(cell a, cell b, int n) // Postcondition: returns the number of paths of length n from a to b. // Precondition: 0 <= n <= 14 // The boolean "memoize" allows us to try it with and without memoization. // The boolean "usingBaseCaseOne" allows us to try it with and without handling the // case n == 1 directly (rather than recursion down to the case n == 0). // // Beyond n = 14, 32 bit ints do not suffice for the counting. { //cout << "ways(" << a << ", " << b << ", " << n << ")" << endl; ++callCount; int w, i; if (n == 0) return (a == b) ? 1 : 0; else if (usingBaseCaseOne && n == 1) return neighbor(a, b) ? 1 : 0; else if ( memoize && (w = memoTable[triple(cellpair(a, b), n)]) != 0 ) // we are using a previous memoization. return w; else // compute and then make a note of it for future use. { for (w = 0, i = 0; i < 6; ++i) w += ways(a, nbr(b, i), n-1); // now the memoization. if (memoize) memoTable[triple(cellpair(a, b), n)] = w; return w; } } // Datatype for the memoTable keys typedef pair cellpair; typedef pair triple; // table for memoization. map < triple, int > memoTable; // vars modifying details of what happens. bool memoize; bool usingBaseCaseOne; int callCount; // for counting the number of calls to ways() function. }; int main(int ac, char* av[]) { cell origin(0,0); if (ac != 4) { cerr << "usage: " << av[0] << " " << endl; cerr << " is the path length (0 <= n <= 14)," << endl; cerr << " (0 or 1) indicates whether to use memoization, m = 1 means yes, and" << endl; cerr << " (0 or 1) indicates whether to use the base case at n = 1." << endl; return 0; } int n = atoi(av[1]); WaysObject ways(*av[2] == '1', *av[3] == '1'); // initialize callCount, memoize, and usingBaseCaseOne. int m = ways(origin, origin, n); cout << "For n = " << n << ", number of ways is " << m << endl; cout << "Number of calls to the ways function is " << ways.callCount << endl; cout << "memoize is " << ways.memoize << ", usingBaseCaseOne is " << ways.usingBaseCaseOne << endl; }