The first attempt is to consider it as a problem of direct dynamic programming.
So, you work from left to right, for each position in the list of envelopes, for each possible set of letters that you can leave, find out the number of ways you could get to this point. Moving forward is easy, you just take the set, you know how many ways to get there, and then for each letter you could add to the next envelope, you increase the sum of the result set by the number of ways to get to this point. When you get to the end of the list of envelopes, you will find how many 0 letters you have left, which is your answer.
In your second example, this may progress as follows:
Step 0: next envelope 1 {1: 2, 2: 2, 3: 1}: 1 -> {1: 2, 2: 1, 3: 1} -> {1: 2, 2: 2} Step 1: next envelope 1 {1: 2, 2: 1, 3: 1}: 1 -> {1: 2, 2: 1} -> {1: 2, 3: 1} {1: 2, 2: 2}: 1 -> {1: 2, 2: 1} Step 2: next envelope 2 {1: 2, 2: 1}: 2 -> {1: 1, 2: 1} {1: 2, 3: 1}: 1 -> {1: 1, 3: 1} -> {1: 2} Step 3: next envelope 2 {1: 1, 2: 1}: 2 -> {2: 1} {1: 1, 3: 1}: 1 -> {1: 1} -> {3: 1} {1: 2}: 1 -> {1: 1} Step 4: next envelope 3 {2: 1}: 2 -> {} {1: 1}: 2 -> {} {3: 1}: 1 // Dead end. Step 5: {}: 4
This works, and you get acquainted with the request for the computational range. At 15, you have 2 ^ 15 = 32768 possible subsets to keep track of, which is very convenient. Somewhere around 20 you will start running out of memory.
Can we improve this? The answer is that we can. The vast majority of our energy is spent remembering, say, whether we used envelopes labeled 8 or envelopes labeled as 9. But we donโt care. What determines how many ways to finish is not whether we used envelope 8 or 9. But rather a sample. How many labels are there that have envelopes x and letters y. Not the tags that how much.
So, if we track only this information, at each step we can grab an envelope with a label with more envelopes left, and if there is a tie, we will choose one with the fewest letters on the left (and if it still remains a tie, we donโt care which one we get). And do the calculations as before, but with much fewer intermediate states. (We do not complete the envelopes, are lined up as you have, but do a stable look on the final letters with envelopes, and you will return the list that you have above.)
We use the notation [xy]: z to denote z marks with x envelopes and y letters on the left. And we have a list of such tags. Then your example 1 1 2 2 3 can be represented as {[2 2]: 2, [1 1]: 1} . And for the transition, we will take one of the labels [2 2] and either use the letter from the other (give us the transition to {[2 1]: 1, [1 2]: 1, [1 1]: 1} ), or we will take one of the labels [2 2] and use the letter from the label [1 1] (giving us the transition to {[2 2]: 1, [1 2]: 1, [1 0]: 1} ).
Carry out a calculation. I will list the states, the number of ways to get there and the transitions you made:
Step 1: {[2 2]: 2, [1 1]: 1}: 1 -> 1 * {[2 1]: 1, [1 2]: 1, [1 1]: 1} -> 1 * {[2 2]: 1, [1 2]: 1, [1 0]: 1} Step 2: {[2 1]: 1, [1 2]: 1, [1 1]: 1}: 1 -> 1 * {[1 1]: 3} -> 1 * {[1 1]: 1, [1 2]: 1, [1 0]: 1} {[2 2]: 1, [1 2]: 1, [1 0]: 1}: 1 -> 1 * {[1 2]: 1, [1 1]: 1, [1 0]: 1} Step 3: {[1 1]: 3}: 1 // This is 2x because once the label is chosen, there are 2 letters to use. -> 2 * {[0 1]: 1, [1 0]: 1, [1 1]: 1} {[1 1]: 1, [1 2]: 1, [1 0]: 1}: 2 -> 1 * {[1 0]: 1, [1 2]: 1, [0 0]: 1} -> 1 * {[1 1]: 2, [0 0]: 1} {[1 2]: 1, [1 1]: 1, [1 0]: 1}: 1 -> 1 * {[1 1]: 2, [0 0]: 1} -> 1 * {[1 2]: 1, [1 0]: 1, [0 0]: 1} Step 4: {[0 1]: 1, [1 0]: 1, [1 1]: 1}: 2 -> 1 * {[1 1]: 1, [0 0]: 2} -> 1 * {[1 0]: 1, [0 1]: 1, [0 0]: 1} {[1 0]: 1, [1 2]: 1, [0 0]: 1}: 2 -> 1 * {[1 1]: 1, [0 0]: 2} {[1 1]: 2, [0 0]: 1}: 2 -> 1 * {[1 0]: 1, [0 1]: 1, [0 0]: 1} Step 5: {[1 1]: 1, [0 0]: 2}: 4 // dead end {[1 0]: 1, [0 1]: 1, [0 0]: 1}: 4 -> 1 * {[0 0]: 3}
So the answer is 4.
This may seem like an insane amount of work - much more than an enumeration. And it was!
However, it scales. Try it with 100 letters and envelopes and it should run fast.