【动态规划】力扣279:完全平方数

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例:

输入:n = 13
输出:2
解释:13 = 4 + 9

方法1:动态规划

class Solution:
    def numSquares(self, n: int) -> int:
        dp = [10000] * (n + 1) # 求最小值,所以dp数组往最大值初始化
        dp[0] = 0 # i=0其实不存在,但是递归会用到,所以赋值为0
        for i in range(n + 1):
            j = 1
            while j ** 2 <= i:
                dp[i] = min(dp[i], dp[i - j ** 2] + 1)
                j += 1
        return dp[n]

看起来很不错,但是超出了时间限制。先算出 j ** 2,让取值范围稍小一些

class Solution:
    def numSquares(self, n: int) -> int:
        # 预处理出 <= sqrt(n) 的完全平方数
        nums = []
        j = 1
        while j * j <= n:
            nums.append(j * j)
            j += 1
        # 转化为完全背包问题【套用「322. 零钱兑换」】
        dp = [n] * (n + 1)    # 初始化为一个较大的值,如 10000 或 n
        dp[0] = 0       # 合法的初始化
        # 完全背包:优化后的状态转移
        for num in nums:                        # 第一层循环:遍历nums
            for i in range(num, n + 1):      # 第二层循环:遍历背包【正序】
                dp[i] = min(dp[i], dp[i - num] + 1)
        return dp[n]

时间复杂度:O(n * sqrt(n)),其中 n 为给定的正整数。状态转移方程的时间复杂度为 O(sqrt(n)),共需要计算 n 个状态,因此总时间复杂度为 O(n * sqrt(n))。
空间复杂度:O(n)。我们需要 O(n) 的空间保存状态。

方法2:数学四平方和定理
四平方和定理证明了任意一个正整数都可以被表示为至多四个正整数的平方和。这给出了本题的答案的上界。
同时四平方和定理包含了一个更强的结论:当且仅当 n ≠ 4^k * (8m+7) 时,n 可以被表示为至多三个正整数的平方和。因此,当 n = 4^k * (8m+7),n 只能被表示为四个正整数的平方和。此时可以直接返回 4。
当 n ≠ 4^k * (8m+7) 时,需要判断到底多少个完全平方数能够表示 n,答案只会是 1,2,3 中的一个:

  • 答案为 1 时,则必有 n 为完全平方数,这很好判断;
  • 答案为 2 时,则有 n=a2+b2,只需要枚举所有的 a(1 ≤ a ≤ \sqrt{n}),判断 n-a^2 是否为完全平方数即可;
  • 答案为 3 时,很难在一个优秀的时间复杂度内解决它,但只需要检查答案为 1 或 2 的两种情况,即可利用排除法确定答案。
class Solution:
    def numSquares(self, n: int) -> int:
        # 判断是否是完全平方数
        def isPerfectSquare(x):
            y = int(sqrt(x))
            return y * y == x
        # 判断是否能表示为 4^k * (8m+7)
        def checkAnswer(x):
            while(x % 4 == 0):
                x /= 4
            return x % 8 == 7
        #主函数
        if isPerfectSquare(n):
            return 1
        if checkAnswer(n):
            return 4
        m = int(sqrt(n)) + 1
        for i in range(m):
            j = n - i ** 2
            if isPerfectSquare(j):
                return 2
        return 3

时间复杂度:O(sqrt(n)),其中 n 为给定的正整数。最坏情况下答案为 3,我们需要运行所有的判断,而判断答案是否为 1 的时间复杂度为 O(1),判断答案是否为 4 的时间复杂度为 O(logn),剩余判断为 O(sqrt(n)),因此总时间复杂度为 O(logn + sqrt(n)=O(sqrt(n))。
空间复杂度:O(1)。我们只需要常数的空间保存若干变量。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/perfect-squares/solution/wan-quan-ping-fang-shu-by-leetcode-solut-t99c/

posted @ 2022-04-23 11:03  Vonos  阅读(152)  评论(0)    收藏  举报