The problem is to find the minimum number of squares required to sum to a number n.
Some examples:
min[ 1] = 1 (1²)
min[ 2] = 2 (1² + 1²)
min[ 4] = 1 (2²)
min[13] = 2 (3² + 2²)
I'm aware of Lagrange's four-square theorem which states that any natural number can be represented as the sum of four squares.
I'm trying to solve this using DP.
This is what I came up with (its not correct)
min[i] = 1 where i is a square number
min[i] = min(min[i - 1] + 1, 1 + min[i - prev]) where prev is a square number < i
What is the correct DP way to solve this?
I'm not sure if DP is the most efficient way to solve this problem, but you asked for DP.
min[i] = min(min[i - 1] + 1, 1 + min[i - prev]) where prev is a square number < i
This is close, I would write condition as
min[i] = min(1 + min[i - prev]) for each square number 'prev <= i'
Note, that for each i
you need to check different possible values of prev
.
Here's simple implementation in Java.
Arrays.fill(min, Integer.MAX_VALUE);
min[0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j*j <= i; ++j) {
min[i] = Math.min(min[i], min[i - j*j] + 1);
}
}
Seems to me that you're close...
You're taking the min() of two terms, each of which is min[i - p] + 1
, where p is either 1 or some other square < i.
To fix this, just take the min() of min[i - p] + 1
over all p (where p is a square < i).
That would be a correct way. There may be a faster way.
Also, it might aid readability if you give min[]
and min()
different names. :-)
P.S. the above approach requires that you memoize min[]
, either explicitly, or as part of your DP framework. Otherwise, the complexity of the algorithm, due to recursion, would be something like O(sqrt(n)!) :-p though the average case might be a lot better.
P.P.S. See @Nikita's answer for a nice implementation. To which I would add the following optimizations... (I'm not nitpicking his implementation -- he presented it as a simple one.)
j*j*2 <= i
or even j*j*4 <= i
. I think so but I haven't got my head completely around it.For large i, it would be faster to compute a limit for j before the inner loop, and compare j directly to it for the loop termination condition, rather than squaring j on every inner loop iteration. E.g.
float sqrti = Math.sqrt(i);
for (int j = 1; j <= sqrti; ++j) {
On the other hand, you need j^2 for the recursion step anyway, so as long as you store it, you might as well use it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With