Leetcode 279 python 完全平方数

题意:

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例一:

输入: n = 12
输出: 3 
解释: 12 = 4 + 4 + 4.

示例二:

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

方法一:

广度优先遍历(图的最短路径)

Leetcode 279 python 完全平方数_第1张图片

举例说明方法:

  1. 例如对于上图中的13,我们要前往0,我们的第一步有两种走法,先走12和先走9。所以我们需要建立一个队列或者栈,然后将第一步的走法压入队列或者栈中。如下(使用队列, 我们同时记录走的步数)

    (12,1),(9,1),(4,1)

  2. 我们将12出队,然后看12的下一步怎么走,发现能走11,8,3,所以我们将
    (11, 2),(8, 2),(3,2)入队。结果如下:
    (9,1),(4,1),(11,2),(8,2),(3,2)

  3. 我们将9出队,然后看9的下一步怎么走,发现能走5,8,0,由于8之前已经访问过,即使现在走8,步数也不会比之前少,所以我们将(5, 2)入队。 当访问0时,我们发现已经到达了0,那么我们更新step+1,然后出循环即可。以下是代码的全部过程:

class Solution:
    def numSquares(self, n):
        """
        :type n: int
        :rtype: int
        """
        q = list()
        q.append([n, 0])
        visited = [False for _ in range(n+1)]
        visited[n] = True

        while any(q):
            num, step = q.pop(0)

            i = 1
            tNum = num - i**2
            while tNum >= 0:
                if tNum == 0:
                    return step + 1

                if not visited[tNum]:
                    q.append((tNum, step + 1))
                    visited[tNum] = True

                i += 1
                tNum = num - i**2

方法二:

四数平方和定理:
a. Lagrange 四平方定理: 任何一个正整数都可以表示成不超过四个整数的平方之和。
b. 满足四数平方和定理的数n(这里要满足由四个数构成,小于四个不行),必定满足 n=4^ a(8b+7) 。
思路:
我们首先将输入的n迅速缩小。然后我们再判断,这个缩小后的数是否可以通过两个平方数的和或一个平方数组成,不能的话我们返回3,能的话我们返回平方数的个数。现在我们的问题已经缩减到了,怎么判断一个数是由一个还是由两个平方数的和构成?对于这个问题,我们当然可以暴力破解。

class Solution:
    def numSquares(self, n):
        """
        :type n: int
        :rtype: int
        """
        while n % 4 == 0:
            n /= 4
        
        if n % 8 == 7: 
            return 4

        a = 0
        while a**2 <= n:
            b = int((n - a**2)**0.5)
            if a**2 + b**2 == n:
                return (not not a) + (not not b)

            a += 1

        return 3

方法三:

动态规划:
通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。
思路:
我们要知道12最少有多少个数构成,实际上如果我们走了一步的话,我们需要知道11、8、3对应的步数,如果我们不走,我们就需要知道12的步数,我们只要通过比较是走0步小,还是走1步哪个更小即可。通过一个式子表示就是

num[n] = min(num[n], num[n-i**2] + 1)
class Solution:
    _dp = [0]
    def numSquares(self, n):
        dp = self._dp
        while len(dp) <= n:
            dp += list((min(dp[-i*i] for i in range(1, int(len(dp)**0.5+1))) + 1,))
        return dp[n]

思路还是和上面一样,但是这里,不是乱加的。为什么要加?这是因为int无法初始化list,我们只有通过加上一个,,将int变成tuple才可以初始化。这里还有一个trick,_dp做成了一个类属性,这有什么用?我们知道我们有很多的测试用例,而这么多的测试用例中肯定有许多相似的测试用例,那么这其中就会有很多相似的计算,如果我们将_dp做成一个类属性的话,类的所有对象都会共有这部分数据,我们就会节省很多的时间。

你可能感兴趣的:(leetcode,python)