This is Dumitru's Intermediate problem, and it is really no harder than the subsequence problem above. The problem is as follows:
You can view this as a graph problem -- the "table" is a graph, and there are only edges going down and right. Your job is to find the maximum weight path through the graph, where the weight of a path is the sum of all the node weights on the path.
Unlike finding the minimum weight path, finding the maximum weight path is an "NP-Complete" problem -- its solution is exponential (or at least, that's the best we can do with current knowledge). However, if we simply view it as a dynamic program, we can solve it without worrying about its running time complexity. Spotting the recursion here is pretty easy. The maximum weight path to the cell at row r and column c is equal to the number apples in the cell, plus the maximum of the maximum weight path to the cell to the left, and the cell above the given cell. If we then find the maximum weight path to the lower right-hand cell, we will have found the maximum weight path through the graph.
The code is in src/apples1.cpp
#include <iostream> #include <vector> #include <string> using namespace std; typedef vector <int> IArray; class Apple { public: int rows; int cols; vector <IArray> apples; int find_max(int r1, int c1); }; int Apple::find_max(int r, int c) { int a; int r1, r2; a = apples[r][c]; if (r == 0 && c == 0) return a; if (r == 0) return a + find_max(r, c-1); if (c == 0) return a + find_max(r-1, c); r1 = find_max(r, c-1); r2 = find_max(r-1, c); return (r1 > r2) ? a+r1 : a+r2; } main(int argc, char **argv) { int r, c; Apple a; if (argc != 3) { cerr << "usage: apples1 rows cols -- apples on standard input\n"; exit(1); } a.rows = atoi(argv[1]); a.cols = atoi(argv[2]); a.apples.resize(a.rows); for (r = 0; r < a.rows; r++) a.apples[r].resize(a.cols); for (r = 0; r < a.rows; r++) { for (c = 0; c < a.cols; c++) { cin >> a.apples[r][c]; if (cin.fail()) { cerr << "Not enough apples\n"; exit(1); } } } cout << a.find_max(a.rows-1, a.cols-1) << endl; } |
We can see it working on some small examples:
UNIX> cat a1.txt 5 10 6 4 UNIX> apples1 2 2 < a1.txt 19 UNIX> cat a2.txt 18 32 88 03 85 29 64 89 88 UNIX> apples1 3 3 < a2.txt 312 UNIX> calc 18+32+85+89+88 312.000000 UNIX>Let's try a bigger example:
UNIX> cat a3.txt 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 UNIX> apples1 10 20 < a3.txt 390 UNIX>Is that right? The best path will be to take the top row all the way to the right and then drop down. That path will have a weight of (19*20)/2 + 20*10 = 390. Yes, that is right. Let's try it with two times the number of rows:
UNIX> cat a3.txt a3.txt | apples1 20 20It hangs, so we must memoize. Again, quite straightforward: (In src/apples2.cpp):
typedef vector <int> IArray; int Apple::find_max(int r, int c) { int a; int r1, r2; int retval; if (cache[r][c] != -1) return cache[r][c]; a = apples[r][c]; if (r == 0 && c == 0) { retval = a; } else if (r == 0) { retval = a + find_max(r, c-1); } else if (c == 0) { retval = a + find_max(r-1, c); } else { r1 = find_max(r, c-1); r2 = find_max(r-1, c); if (r1 > r2) { retval = a+r1; } else { retval = a+r2; } } cache[r][c] = retval; return retval; } |
UNIX> cat a3.txt a3.txt | apples2 20 20 590 UNIX> cat a3.txt a3.txt a3.txt a3.txt a3.txt a3.txt a3.txt a3.txt a3.txt a3.txt | apples2 100 20 2190Nice. In src/apples3.cpp, we remove the recursion. We do so by starting at the beginning of the cache and filling in to the higher values:
int Apple::find_max() { int r1, r2; int retval; int r, c; cache[0][0] = apples[0][0]; for (r = 1; r < rows; r++) cache[r][0] = apples[r][0] + cache[r-1][0]; for (c = 1; c < cols; c++) cache[0][c] = apples[0][c] + cache[0][c-1]; for (r = 1; r < rows; r++) { for (c = 1; c < cols; c++) { r1 = cache[r][c-1]; r2 = cache[r-1][c]; if (r1 > r2) { cache[r][c] = apples[r][c]+r1; } else { cache[r][c] = apples[r][c]+r2; } } } return cache[rows-1][cols-1]; } |
As with the maximum subsequence problem, we can reduce the cache size to two rows. I won't do it here -- see if you can do it yourself!