You are given a score
$S.You can win basketball points e.g. 1 point, 2 points and 3 points.
Write a script to find out the different ways you can score
$S.
Input: $S = 4 Output: 1 1 1 1 1 1 2 1 2 1 1 3 2 1 1 2 2 3 1 Input: $S = 5 Output: 1 1 1 1 1 1 1 1 2 1 1 2 1 1 1 3 1 2 1 1 1 2 2 1 3 1 2 1 1 1 2 1 2 2 2 1 2 3 3 1 1 3 2
The Tribonacci Numbers are defined as follows:
\[ \mathcal{T}(n) = \begin{cases} 0 & \text{if } n \leq 1 \\ 1 & \text{if } n = 2 \\ \mathcal{T}(n - 3) + \mathcal{T}(n - 2) + \mathcal{T}(n - 1) & \text{if } n > 2 \end{cases} \]
This sequence is found at the OEIS as A000073.
There is a formula to calculate \(\mathcal{T}(n)\) directly, in a similar was there is one for the Fibonacci numbers:
\[ \mathcal{T}(n) = \left\lfloor \frac{3 \left(\sqrt[3]{586 + 102\sqrt{33}}\right) \left(\frac{1}{3}(\sqrt[3]{19 + 3\sqrt{33}} + \sqrt[3]{19 - 3\sqrt{33}} + 1)\right)^n} {\left(\sqrt[3]{586 + 102\sqrt{33}}\right)^2 - 2\sqrt[3]{586 + 102\sqrt{33}} + 4} \right\rceil \]
Now, the number of ways to decompose a non-negative integer \(N\) as a sum
of 1s, 2s, and 3s, is equal to \(\mathcal{T}(N + 2)\).
But we don't have to calculate the number of ways to decompose a score, we actually have to calculate all the different ways to decompose a given score.
The definition of the Tribonacci Numbers gives us a way to calculate the all the different decompositions. Let \(\mathcal{S}(n)\) be the set of all decompositions of a score of \(n - 2\). Then
\[ \mathcal{S}(n) = \begin{cases} \emptyset & \text{if } n \leq 1, \\ \{ \text{""} \} & \text{if } n = 2 \\ \{ \forall x \in \mathcal{S}(n - 1): \text{"1"} \odot x \} \; \cup & \\ \{ \forall x \in \mathcal{S}(n - 2): \text{"2"} \odot x \} \; \cup & \text{if } n > 2 \\ \{ \forall x \in \mathcal{S}(n - 3): \text{"3"} \odot x \} & \end{cases} \]
where \(\odot\) is the concatenation operator.
That is, we can decompose a score of \(n\) by either first scoring a \(1\) and then decomposing a score of \(n - 1\), or first scoring a \(2\) and then decomposing a score of \(n - 2\), or first scoring a \(3\) and then decomposing a score of \(n - 3\).
The definition given above suggests using a recursive solution. This is possible, but instead, we will be using an iterative solution.
We will reading the n from standard input.
We start off by initializing the first three sets, \(\mathcal{S}(0), \mathcal{S}(1), \mathcal{S}(2)\):
my @s = ([], [], [""]);
Thus two empty sets, and a set consisting of an empty string.
We now repeatedly (n times) add a next set, using the last three
sets:
map {push @s => [map {my $s = $_; map {"$s $_"} @{$s [-$s]}} 1 .. 3]} 1 .. <>;
We can rewrite this using nested for loops instead of maps to make
it clear what is happening:
for (1 .. <>) {
my @new;
for my $s (1 .. 3) {
for my $decomposition (@{$s [-$s]}) {
push @new => "$s $decomposition";
}
}
push @s => \@new;
}
A new set is created by taking all the decompositions for the last
three sets, and prepending them with 1, 2, or 3.
At the end, we have to print the elements of the last set:
say for @{$s [-1]}
Find the full program on GitHub.
We will be using two arrays, c, and s. s [i] will contain
all the decompositions of a score of i, while c [i] will contain
the number of decompositions of a score of i.
First, the initialization:
c [0] = 0
c [1] = 0
c [2] = 1
s [2, 0] = ""
We can now repeatedly add new entries to s and c. Note that we have
n in $1:
for (i = 3; i < $1 + 3; i ++) {
c [i] = 0
for (j = 1; j <= 3; j ++) {
for (k = 0; k < c [i - j]; k ++) {
s [i, c [i]] = j " " s [i - j, k]
c [i] ++
}
}
}
Finally, we print the result:
for (k = 0; k < c [$1 + 2]; k ++) {
print s [$1 + 2, k]
}
Find the full program on GitHub.
For our Bash solution, we need a trick. Bash doesn't have multidimensional arrays. We could have used a concatenated key, and an associative array, but we do something else instead. Instead of having an array of sets, we use an array of strings; each string has all the decompositions concatenated together; and each decomposition starts with a newline. (So, the newline acts as a separator, but there is an extra newline at the beginning).
The initialization:
declare scores
l=$'\n'
scores[2]=$l
l=$'\n' is a little trick to get a string consisting of just a newline
into a variable.
We can now repeatedly add a new entry to scores:
for ((i = 3; i < n + 3; i ++))
do for ((j = 1; j <= 3; j ++))
do scores[$i]=${scores[$i]}${scores[$((i - j))]//$l/$l$j }
done
done
The interesting part is: ${scores[$((i - j))]//$l/$l$j }. i is the
index of new entry, and j is 1, 2, or 3, so ${scores[$((i - j))]}
is one of the last three entries. We then use a regular expression to
prepend j to each of the decompositions. The general syntax is:
${word//pattern/replacement}. This takes $word, and replaces each
non-overlapping occurance of pattern with replacement, returning
the result. The Perl equivalent would be: $word =~ s/pattern/replacement/gr.
Printing the result is now simply — we don't have to loop as the decompositions are already separated by newlines. But we have to remove the first newline:
echo "${scores[$((n + 2))]/$l/}"
Note that we have only a single slash before the pattern; this means we only replace the first occurance.
Find the full program on GitHub.
In C, we have to work hard! Since C doesn't have a way of find out the size of an array, nor an efficient method to find the length of a string, we will be using three arrays:
scores, a two dimensional array of strings (which are pointers to char)count, which counts the number of decompositions for a specific scorelengths, a two dimensional array, with the length of each of the
strings in scores.We'll declare this as:
typedef long long number;
char *** scores;
number * count;
size_t ** lengths;
Then we read our number n, and allocate space for the arrays:
int n;
if (scanf ("%d", &n) != 1) {
perror ("Unexpected input");
exit (1);
}
if ((scores = (char ***) malloc ((n + 3) * sizeof (char **))) == NULL) {
perror ("Malloc scores failed");
exit (1);
}
if ((count = (number *) malloc ((n + 3) * sizeof (number))) == NULL) {
perror ("Malloc count failed");
exit (1);
}
if ((lengths = (size_t **) malloc ((n + 3) * sizeof (size_t *))) == NULL) {
perror ("Malloc lengths failed");
exit (1);
}
We can now initialize the first three entries, which requires more allocating of memory:
count [0] = 0;
count [1] = 0;
count [2] = 1;
if ((scores [2] = (char **) malloc (sizeof (char *))) == NULL) {
perror ("Malloc failed");
exit (1);
}
if ((scores [2] [0] = (char *) malloc (sizeof (char))) == NULL) {
perror ("Malloc failed");
exit (1);
}
if ((lengths [2] = (size_t *) malloc (sizeof (size_t))) == NULL) {
perror ("Malloc failed");
exit (1);
}
scores [2] [0] [0] = '\0';
lengths [2] [0] = 0;
We now start the loop in which we add new sets to scores:
for (int i = 3; i < n + 3; i ++) {
We start with calculating how many entries there will be in that
set (which is the sum of the sizes of the previous three sets),
and allocate memory for scores [i] and lengths [i]:
count [i] = count [i - 1] + count [i - 2] + count [i - 3];
if ((scores [i] = (char **) malloc (count [i] * sizeof (char *)))
== NULL) {
perror ("Malloc failed");
exit (1);
}
if ((lengths [i] = (size_t *) malloc (count [i] * sizeof (size_t)))
== NULL) {
perror ("Malloc failed");
exit (1);
}
It's only now that we can create the entries in the set. Each entry is
two character longer than the its "parent" entry: first a 1, 2,
or 3, then a space, then a copy of an entry from one of the three
previous sets.
number l = 0;
for (int j = 1; j <= 3; j ++) {
for (int k = 0; k < count [i - j]; k ++) {
lengths [i] [l] = 2 + lengths [i - j] [k];
if ((scores [i] [l] = (char *) malloc
((lengths [i] [l] + 1) * sizeof (char))) == NULL) {
perror ("Malloc failed");
exit (1);
}
scores [i] [l] [0] = j + '0';
scores [i] [l] [1] = ' ';
strncpy (scores [i] [l] + 2, scores [i - j] [k],
lengths [i - j] [k]);
scores [i] [l] [lengths [i] [l]] = '\0';
l ++;
}
}
At the end of the loop, we can release some memory — we don't need the entry three steps back anymore:
if (i - 3 > 1) {
for (int k = 0; k < count [i - 3]; k ++) {
free (scores [i - 3] [k]);
}
free (scores [i - 3]);
free (lengths [i - 3]);
}
After we have created the final set, we can print its entries:
for (int i = 0; i < count [n + 2]; i ++) {
printf ("%s\n", scores [n + 2] [i]);
}
All what now needs to be done, is freeing memory:
for (int i = n; i <= n + 2; i ++) {
for (int k = 0; k < count [i]; k ++) {
free (scores [i] [k]);
}
free (scores [i]);
free (lengths [i]);
}
free (scores);
free (lengths);
free (count);
Find the full program on GitHub.
We have also solutions in Lua, Node.js, Python and Ruby, which are all similar to our Perl solution.