I am working on a code challenge problem -- \"find lucky triples\". \"Lucky triple\" is defined as \"In a list lst
, for any combination of triple like (ls
A simple dynamic programming-like algorithm will do this in quadratic time and linear space. You just have to maintain a counter c[i]
for each item in the list, that represents the number of previous integers that divides L[i]
.
Then, as you go through the list and test each integer L[k]
with all previous item L[j]
, if L[j]
divides L[k]
, you just add c[j]
(which could be 0) to your global counter of triples, because that also implies that there exist exactly c[j]
items L[i]
such that L[i]
divides L[j]
and i < j
.
int c[] = {0}
int nbTriples = 0
for k=0 to n-1
for j=0 to k-1
if (L[k] % L[j] == 0)
c[k]++
nbTriples += c[j]
return nbTriples
There may be some better algorithm that uses fancy discrete maths to do it faster, but if O(n^2) is ok, this will do just fine.
In regard to your comment:
Why DP? We have something that can clearly be modeled as having a left to right order (DP orange flag), and it feels like reusing previously computed values could be interesting, because the brute force algorithm does the exact same computations a lot of times.
How to get from that to a solution? Run a simple example (hint: it should better be by treating input from left to right). At step i
, compute what you can compute from this particular point (ignoring everything on the right of i), and try to pinpoint what you compute over and over again for different i's
: this is what you want to cache. Here, when you see a potential triple at step k
(L[k] % L[j] == 0
), you have to consider what happens on L[j]
: "does it have some divisors on its left too? Each of these would give us a new triple. Let's see... But wait! We already computed that on step j
! Let's cache this value!" And this is when you jump on your seat.