The approach I'd probably take would look like this:
Create an array ("X
") of (N+1)xM
elements, where N
= the "width" of your 2D array and M
= the number of symbols in your list.
Set X[0][i] = 0
for all values i (in the range of M
). This is because the maximum possible sum of the first 0 chosen items is 0. We can also fill in X[a][b]
as 0 for any b>=a
since those positions are ones that we couldn't possible get to (we wouldn't have picked enough symbols to be at that symbol yet). Similarly, we can set a "triangle" of values at the tail end of the array to 0 as well, since in order to be at one of those indices we'd have to pick too many repetitions of earlier symbols to be able to pick at least one item for later symbols.
Now, set X[1][i]
to be equal to the maximum possible sum of the first 1 chosen elements if the current symbol being chosen is symbol i
in the sequence. This is fairly trivial to compute - it's just the corresponding value for that symbol from the array.
Now, set X[2][i]
to be equal to the maximum possible sum of the first 2 chosen elements if the current symbol being chosen is symbol i
in the sequence. This is also semi-trivial, though not as simple as [1] was - after all, now it's the corresponding value of the symbol plus the maximum of either X[1][i-1]
or X[1][i]
- because we could either begin the current symbol at this position, or have already begun it at an earlier position.
Continue the algorithm for each X[k]
(setting X[k][i]
equal to the max of either X[k-1][i-1]
or X[k-1][i]
, plus the corresponding symbol value for the current i
) until k
reaches N
. The maximum value in your X[k]
column is your maximum result.
Since overall you only need to look at each array element a constant number of times, the overall algorithm runs in O(MN)
time.
(Note that technically you don't need the full NxM array in memory all the time, only the previous column and the current column. It's just easier to explain it in terms of the full array. Thus the optimized version of this algorithm uses O(M)
memory.)
Example result array from your example:
0 1 2 3 4 5 6 7 8 9 10 11 12 13
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0 - C | 0| 1| 2| 3| 6|10|13|22|23|24| 0| 0| 0| 0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
1 - A | 0| 0| 2| 3| 7|10|13|17|24|26|27| 0| 0| 0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
2 - B | 0| 0| 0| 7| 9|10|11|14|18|25|26|28| 0| 0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3 - D | 0| 0| 0| 0|12|16|19|21|23|27|32|38|44| 0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4 - C | 0| 0| 0| 0| 0|16|19|28|29|30|31|33|36|45|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
If you store (at each array position) which previous symbol was used as part of that position's total, it's easy to then just read back through the path that traces from the bottom-right corner to the top-left to figure out what the maximal sequence is. Alternatively, you can simply trace back by looking at which of the two values - left or up-left - is the larger from your current position. In this case, the maximal sequence is CABDDDDDDDDDC.