求和为 n 的最小完全平方数

Finding the minimum number of perfect squares to sum up to n

我正在尝试解决找到总和为 n

的最小完全平方数(即 1、2、4、9..)的问题

这是我的递归自上而下方法:

import math

class Solution:
    def numSquares(self, n: int) -> int:
        
        dp = [math.inf] * (n + 1)
        dp[0] = 0
        dp[1] = 1 

        def solve(n: int):
            if dp[n] != math.inf:
                return dp[n]

            for i in range(n, 0, -1):
                if n - i * i >= 0:
                    sol = solve(n - i*i)
                    dp[i] = min(1 + sol, dp[i])

            return dp[n]

        solve(n)
        print(dp)

Solution().numSquares(12)

我不明白为什么这段代码没有产生正确的结果。你能帮我找到错误吗?

谢谢!

class Solution:
    def numSquares(self, n: int) -> int:
        dp = [math.inf] * (n + 1)
        dp[0] = 0
        dp[1] = 1
        
        def solve(n: int):
            if dp[n] != math.inf:
                return dp[n]
            sol = math.inf
            for i in range(n, 0, -1):
                if n - i * i >= 0:
                    sol = min(sol, solve(n-i*i))
            dp[n] = sol+1
            
            return dp[n]
        
        return solve(n)

以上是您解决方案的更正版本,但它仍然太慢,因为您检查每个数字,即使它们的平方明显大于 n。下面是经过LC时间限制的优化版本:

class Solution:
    def numSquares(self, n: int) -> int:
        dp = [math.inf] * (n + 1)
        dp[0] = 0
        dp[1] = 1
        
        def solve(n: int):
            if dp[n] != math.inf:
                return dp[n]
            sol = math.inf
            for i in range(1, n):
                if n - i * i >= 0:
                    sol = min(sol, solve(n-i*i))
                else:
                    break
            dp[n] = sol+1
            
            return dp[n]
        
        return solve(n)

但是还有优化的空间。一点点:

class Solution:
    def numSquares(self, n: int) -> int:
        dp = [math.inf] * (n + 1)
        dp[0] = 0
        dp[1] = 1
        
        def solve(n: int):
            if dp[n] != math.inf:
                return dp[n]
            sol = math.inf
            for i in range(1, math.floor(n**(1/2))+1):
                sol = min(sol, solve(n-i*i))
            dp[n] = sol+1
            
            return dp[n]
        
        return solve(n)

解决这个问题的另一种方法是使用 BFS 方法。将到达 n 的所有路径想象成一棵树,其中叶子的值为 n 并且每次移动到相邻节点的成本为 1。在 BFS 中,第一次命中是最佳命中,因为成本都相等:

class Solution:
    def numSquares(self, n: int) -> int:
        deq = collections.deque([n])
        steps = 1
        
        while deq:
            n = len(deq)
            
            for _ in range(n):
                node = deq.popleft()
                for i in range(1, floor(node**(1/2))+1):
                    num = i**2
                    if num == node:
                        return steps
                    deq.append(node - num)
            steps += 1

正如 Dmitry Bychenko 所指出的,您可以使用拉格朗日定理解决该问题。这是关于它的nice write up and python solution