Skip to content

279. Perfect Squares

#244

Problem link

https://leetcode.com/problems/perfect-squares

Problem Summary

1부터 10000까지의 수가 주어질 때 더해서 만들 수 있는 최소 제곱수의 개수를 구하는 문제.

Solution

딱 보니 전형적인 DP 문제이다. (medium 난이도는 대부분이 DP 같다...)
dp[i] = 최소 제곱수의 개수로 정의하고 j를 제곱수라고 했을 때

dp[i] = min(dp[i], dp[i - j] + 1)

시간 복잡도는 i에 대해 한번, j에 대해 한번 돌게 되므로 O (n ^ 1.5) 가 된다. (제곱수는 n ^ 0.5번만 돌면 된다) 근데 실제 제출했을 때 top down, bottom up 둘다 생각보다 느린데 알고보니 수학적으로 O(n ^ 0.5)로 풀 수 있다...

라그랑주의 네 제곱수 정리 (Lagrange's Four Square theorem) 가 존재하는데 모든 자연수는 최대 4개의 제곱수로 만들 수 있다는 정리이다. 즉 이 문제의 답은 1, 2, 3, 4 중에 하나.
또한 추가로 제곱수 정리들을 더 활용하면 수학적으로 O(n ^ 0.5)로 풀 수는 있다... 만 재미 및 참고용으로만 보자. (코딩 인터뷰때 이렇게 풀면 라그랑주의 네 제곱수 정리를 증명해야 할 것...)

Source Code

Top-Down DP (7680ms)

class Solution:
    def numSquares(self, n: int) -> int:
        squares = []
        for i in range(1, 100):
            squares.append(i * i)

        @cache
        def solve(n):
            if n == 0:
                return 0
            if n < 0:
                return 987654321

            res = 987654321
            for s in squares:
                res = min(res, solve(n - s) + 1)

            return res

        return solve(n)

Bottom-Up DP (2930ms)

from math import sqrt


class Solution:
    def numSquares(self, n: int) -> int:
        dp = [0]
        for i in range(1, n + 1):
            dp.append(i)

        for i in range(1, int(sqrt(n))+1):
            for j in range(2, n + 1):
                if j - i * i >= 0:
                    dp[j] = min(dp[j], dp[j - i * i] + 1)

        return dp[n]

Mathematically (39ms)

class Solution:    
    def is_perfect_square(self, n):
        root = int(math.sqrt(n))
        return root*root == n

    def numSquares(self, n: int) -> int:
         # Check if n is a perfect square
        if self.is_perfect_square(n):
            return 1
        
        # Check the sum of two squares theorem by trying every square less than n
        for i in range(1, int(n**0.5) + 1):
            if self.is_perfect_square(n - i*i):
                return 2
        
        # The four-square and three-square theorems - Check if the number can be expressed as the sum of three squares
        # This checks if it's NOT of the form 4^a(8b + 7) for any non-negative integers a and b
        while n % 4 == 0:
            n /= 4
        if n % 8 == 7:
            return 4

        return 3