4

It was one of my interview question, and I could not think of the good way to get number N. (plus, I did not understand the American football scoring system as well)

6 points for the touchdown
1 point for the extra point (kicked)
2 points for a safety or a conversion (extra try after a touchdown)
3 points for a field goal

What would be an efficient algorithm to get all combinations of point-accumulations necessary to get a certain score N?

amit
  • 175,853
  • 27
  • 231
  • 333
user482594
  • 16,878
  • 21
  • 72
  • 108
  • 1
    are you trying to print these combinations or get the number of them? – amit Feb 09 '12 at 07:38
  • 1
    The answers below are good, but ignore the fact that the 1-point play can only come following a touchdown. As such, you'll need to tweak the answers to account for that. – dlev Feb 09 '12 at 08:02
  • 1
    @dlev - well, not all of us are american... our football has but one point goals. – WeaselFox Feb 09 '12 at 08:18
  • 2
    @dlev: To adjust for special rules such as "1 may only follow a 6", you can simply change the 1 to a 7 so that it includes the 6 that must come before it. – tom Feb 09 '12 at 09:34
  • 1
    Should a touch down followed by a field goal be counted differently from a field goal followed by a touch down? Also, must a safety or a conversion follow a touch down immediately? These will drastically change the solution.. – aelguindy Feb 09 '12 at 10:17

7 Answers7

3

Assuming here you are looking for a way to get number of possibilities and not the actual possibilities.

First let's find a recursive function:

f(n) = (f(n-6) >= 0? f(n-6) : 0) + (f(n-1) >= 0 ? f(n-1) : 0) + (f(n-2) >= 0 ? f(n-2) : 0) + (f(n-3) >= 0 ? f(n-3) : 0)

base: f(0) = 1 and f(n) = -infinity [n<0]

The idea behind it is: You can always get to 0, by a no scoring game. If you can get to f(n-6), you can also get to f(n), and so on for each possibility.

Using the above formula one can easily create a recursive solution.

Note that you can even use dynamic programming with it, initialize a table with [-5,n], init f[0] = 0 and f[-1] = f[-2] = f[-3] = f[-4] = f[-5] = -infinity and iterate over indexes [1,n] to achieve the number of possibilities based on the the recursive formula above.

EDIT:
I just realized that a simplified version of the above formula could be:
f(n) = f(n-6) + f(n-1) + f(n-2) + f(n-3)
and base will be: f(0) = 1, f(n) = 0 [n<0]
The two formulas will yield exactly the same result.

Community
  • 1
  • 1
amit
  • 175,853
  • 27
  • 231
  • 333
  • I'm not sure if this can be the right answer to the question asked. You can get +2 only after a touch down. This formula does not seem to handle that. – user2349990 Apr 28 '16 at 19:37
  • @user2349990 I am not familiar with football rules, but in this case, you can basically get from a touchdown 6 or 6+2 (for the bonus), in this case, you simply replace the formula with `f(n) = f(n-6) + f(n-8) + f(n-1) + f(n-3)`, where the `f(n-8)` represents getting a touchdown AND the bonus, and `f(n-6)` represents getting the touchdown without the bonus. Hopes that makes sense. – amit Apr 29 '16 at 16:32
  • Yes. This formula looks like the correct answer to this question. It is similar to the staircase problem. – user2349990 Apr 29 '16 at 22:12
  • I've provided a Java implementation of this DP solution in my answer below. – stackoverflowuser2010 Dec 10 '16 at 23:28
  • @user2349990 you can also score 2 points by getting a safety. – Cole Jan 09 '17 at 17:37
1

This is identical to the coin change problem, apart from the specific numbers used. See this question for a variety of answers.

Community
  • 1
  • 1
Michael J. Barber
  • 24,518
  • 9
  • 68
  • 88
0

I know this question is old, but all of the solutions I see help calculate the number of scoring permutations rather than the number of scoring combinations. (So I think either something like this should be an answer or the question title should be changed.)

Some code such as the following (which could then be converted into a dp) will calculate the number of possible combinations of different scores:

int getScoreCombinationCount(int score, int scoreVals[], int scoreValIndex) {
    if (scoreValIndex < 0)
        return 0;
    if (score == 0)
        return 1;
    if (score < 0)
        return 0;

return getScoreCombinationCount(score - scoreVals[scoreValIndex], scoreVals, scoreValIndex) +
       getScoreCombinationCount(score, scoreVals, scoreValIndex - 1);
}
Jared Sohn
  • 443
  • 5
  • 17
0

This solution, implemented based on a solution in the book Elements of Programming Interviews seems to be correct for counting the number of 'combinations' (no duplicate sets) for a set of score points.

For example, if points = {7, 3, 2}, there are 2 combinations for a total score of 7:

{7} and {3, 2, 2}.

public static int ScoreCombinationCount(int total, int[] points)
{
    int[] combinations = new int[total + 1];
    combinations[0] = 1;
    for (var i = 0; i < points.Length; i++)
    {
        int point = points[i];
        for (var j = point; j <= total; j++)
        {
            combinations[j] += combinations[j - point];
        }
    }

    return combinations[total];
}

I am not sure I understand the logic though. Can someone explain?

  • This code is correct, but it outputs the number of UNIQUE combinations to get to the total score. In your example with `points={7,3,2}` and `total=7`, you will indeed get 2 ways: `{7}` and `{3,2,2}`. However, if you count all combinations, then you will get 4 ways: `{7}, {3,2,2}, {2,3,2}, {2,2,3}`. – stackoverflowuser2010 Dec 10 '16 at 22:32
0

The answer to this question depends on whether or not you allow the total number of combinations to include duplicate unordered combinations.

For example, in American football, you can score 2, 3, or 7 points (yes, I know you can miss the extra point on a touchdown, but let's ignore 1 point).

Then if your target N is 5, then you can reach it with {2, 3} or {3, 2}. If you count that as two combinations, then the Dynamic Programming solution by @amit will work. However, if you count those two combinations as one combination, then the iterative solution by @Maximus will work.

Below is some Java code, where findWays() corresponds to counting all possible combinations, including duplicates, and findUniqueWays() corresponds to counting only unique combinations.

// Counts the number of non-unique ways to reach N.
// Note that this algorithm counts {1,2} separately from {2,1}
// Applies a recurrence relationship. For example, with values={1,2}:
// cache[i] = cache[i-1] + cache[i-2]
public static long findWays(int N, int[] values) {

    long cache[] = new long[N+1];
    cache[0] = 1;

    for (int i = 1; i <= N; i++) {
        cache[i] = 0;
        for (int value : values) {
            if (value <= i)
                cache[i] += cache[i-value];
        }
    }

    return cache[N];
}

// Counts the number of unique ways to reach N.
// Note that this counts truly unique combinations: {1,2} is the same as {2,1}
public static long findUniqueWays(int N, int[] values) {

    long [] cache = new long[N+1];
    cache[0] = 1;

    for (int i = 0; i < values.length; i++) {

        int value = values[i];

        for (int j = value; j <= N; j++) {
            cache[j] += cache[j-value];
        }
    }

    return cache[N];
}

Below is a test case where the possible points are {2,3,7}.

private static void testFindUniqueWaysFootball() {

    int[] points = new int[]{2, 3, 7}; // Ways of scoring points.
    int[] NValues = new int[]{5, 7, 10}; // Total score.
    long result = -1;

    for (int N : NValues) {
        System.out.printf("\nN = %d points\n", N);

        result = findWays(N, points);
        System.out.printf("findWays() result = %d\n", result);

        result = findUniqueWays(N, points);
        System.out.printf("findUniqueWays() result = %d\n", result);
    }
}

The output is:

N = 5 points
findWays() result = 2
findUniqueWays() result = 1

N = 7 points
findWays() result = 4
findUniqueWays() result = 2

N = 10 points
findWays() result = 9
findUniqueWays() result = 3

The results above show that to reach N=7 points, then there 4 non-unique ways to do so (those ways are {7}, {2,2,3}, {2,3,2}, {3,2,2}). However, there are only 2 unique ways (those ways are {7} and {2,2,3}). However, .

Community
  • 1
  • 1
stackoverflowuser2010
  • 38,621
  • 48
  • 169
  • 217
0

Below is a python program to find all combinations ignoring the combination order (e.g. 2,3,6 and 3,2,6 are considered one combination). This is a dynamic programming solution with order(n) time. Scores are 2,3,6,7.

We traverse from row score 2 to row score 7 (4 rows). Row score 2 contains the count if we only consider score 2 in calculating the number of combinations. Row score 3 produces each column by taking the count in row score 2 for the same final score plus the previous 3 count in its own row (current position minus 3). Row score 6 uses row score 3, which contains counts for both 2,3 and adds in the previous 6 count (current position minus 6). Row score 7 uses row score 6, which contains counts for row scores 2,3,6 plus the previous 7 count.

For example, numbers[1][12] = numbers[0][12] + numbers[1][9] (9 = 12-3) which results in 3 = 1 + 2; numbers[3][12] = numbers[2][12] + numbers[3][9] (9 = 12-3) which results in 7 = 6 + 1;

def cntMoney(num):
    mSz = len(scores)
    numbers = [[0]*(1+num) for _ in range(mSz)]
    for mI in range(mSz): numbers[mI][0] = 1
    for mI,m in enumerate(scores):
        for i in range(1,num+1):
            numbers[mI][i] = numbers[mI][i-m] if i >= m else 0
            if mI != 0: numbers[mI][i] += numbers[mI-1][i]
        print('m,numbers',m,numbers[mI])
    return numbers[mSz-1][num]

scores = [2,3,6,7]
num = 12
print('score,combinations',num,cntMoney(num))   

output:
('m,numbers', 2, [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
('m,numbers', 3, [1, 0, 1, 1, 1, 1, 2, 1, 2, 2, 2, 2, 3])
('m,numbers', 6, [1, 0, 1, 1, 1, 1, 3, 1, 3, 3, 3, 3, 6])
('m,numbers', 7, [1, 0, 1, 1, 1, 1, 3, 2, 3, 4, 4, 4, 7])
('score,combinations', 12, 7)

Below is a python program to find all ordered combinations (e.g. 2,3,6 and 3,2,6 are considered two combinations). This is a dynamic programming solution with order(n) time. We build up from the start, adding the combinations calculated from previous score numbers, for each of the scores (2,3,6,7).

'vals[i] += vals[i-s]' means the current value equals the addition of the combinations from the previous values for the given scores. For example, for column vals[12] = the addition of scores 2,3,6,7: 26 = 12+9+3+2 (i-s = 10,9,6,5).

def allSeq(num):
    vals = [0]*(num+1)
    vals[0] = 1
    for i in range(num+1):
        for s in scores:
            if i-s >= 0: vals[i] += vals[i-s]
    print(vals)
    return vals[num]

scores = [2,3,6,7]
num = 12
print('num,seqsToNum',num,allSeq(num))

Output:
[1, 0, 1, 1, 1, 2, 3, 4, 6, 9, 12, 18, 26]
('num,seqsToNum', 12, 26)

Attached is a program that prints the sequences for each score up to the given final score.

def allSeq(num):
    seqs = [[] for _ in range(num+1)]
    vals = [0]*(num+1)
    vals[0] = 1
    for i in range(num+1):
        for sI,s in enumerate(scores):
            if i-s >= 0:
                vals[i] += vals[i-s]
                if i == s: seqs[i].append(str(s))
                else:
                    for x in seqs[i-s]:
                        seqs[i].append(x + '-' + str(s))


    print(vals)
    for sI,seq in enumerate(seqs):
        print('num,seqsSz,listOfSeqs',sI,len(seq),seq)
    return vals[num],seqs[num]

scores = [2,3,6,7]
num = 12
combos,seqs = allSeq(num)

Output:
[1, 0, 1, 1, 1, 2, 3, 4, 6, 9, 12, 18, 26]
('num,seqsSz,listOfSeqs', 0, 0, [])
('num,seqsSz,listOfSeqs', 1, 0, [])    
('num,seqsSz,listOfSeqs', 2, 1, ['2'])
('num,seqsSz,listOfSeqs', 3, 1, ['3'])
('num,seqsSz,listOfSeqs', 4, 1, ['2-2'])
('num,seqsSz,listOfSeqs', 5, 2, ['3-2', '2-3'])
('num,seqsSz,listOfSeqs', 6, 3, ['2-2-2', '3-3', '6'])
('num,seqsSz,listOfSeqs', 7, 4, ['3-2-2', '2-3-2', '2-2-3', '7'])
('num,seqsSz,listOfSeqs', 8, 6, ['2-2-2-2', '3-3-2', '6-2', '3-2-3', '2-3-3', '2-6'])
('num,seqsSz,listOfSeqs', 9, 9, ['3-2-2-2', '2-3-2-2', '2-2-3-2', '7-2', '2-2-2-3', '3-3-3', '6-3', '3-6', '2-7'])
('num,seqsSz,listOfSeqs', 10, 12, ['2-2-2-2-2', '3-3-2-2', '6-2-2', '3-2-3-2', '2-3-3-2', '2-6-2', '3-2-2-3', '2-3-2-3', '2-2-3-3', '7-3', '2-2-6', '3-7'])
('num,seqsSz,listOfSeqs', 11, 18, ['3-2-2-2-2', '2-3-2-2-2', '2-2-3-2-2', '7-2-2', '2-2-2-3-2', '3-3-3-2', '6-3-2', '3-6-2', '2-7-2', '2-2-2-2-3', '3-3-2-3', '6-2-3', '3-2-3-3', '2-3-3-3', '2-6-3', '3-2-6', '2-3-6', '2-2-7'])
('num,seqsSz,listOfSeqs', 12, 26, ['2-2-2-2-2-2', '3-3-2-2-2', '6-2-2-2', '3-2-3-2-2', '2-3-3-2-2', '2-6-2-2', '3-2-2-3-2', '2-3-2-3-2', '2-2-3-3-2', '7-3-2', '2-2-6-2', '3-7-2', '3-2-2-2-3', '2-3-2-2-3', '2-2-3-2-3', '7-2-3', '2-2-2-3-3', '3-3-3-3', '6-3-3', '3-6-3', '2-7-3', '2-2-2-6', '3-3-6', '6-6', '3-2-7', '2-3-7'])
~
JayS
  • 2,057
  • 24
  • 16
0

You could use dynamic programming loop from 1 to n, here is some pseudo code:

results[1] = 1
for i from 1 to n :
   results[i+1]   += results[i]
   results[i+2]   += results[i]
   results[i+3]   += results[i]
   results[i+6]   += results[i]

this way complexity is O(N), instead of exponential complexity if you compute recursively by subtracting from the final score... like computing a Fibonacci series.

I hope my explanation is understandable enough..

WeaselFox
  • 7,220
  • 8
  • 44
  • 75
  • Top-down DP (normal recursion) can be turned to O(n) with caching, so that's not an issue. The advantage of your bottom-up DP is that a sliding window can be used to make the space complexity O(m), where m is the maximum of the goal points (in this case 6). – tom Feb 09 '12 at 09:25
  • This pseudo-code is a bit sloppy. You will need to allocate your `results[]` array to be of size `n+6`, or you need to iterate with `for i from 1 to (n-6)`. – stackoverflowuser2010 Dec 10 '16 at 23:32