题目

https://leetcode-cn.com/problems/perfect-squares/
给定正整数n,找到若干个完全平方数(比如1, 4, 9, 16,……)使得他们的和等于n。你需要让组成和的完全平方数的个数最少。
image.png

思路

思路一:暴力枚举法

重点:枚举所有可能的组合,并找到完全平方数的个数最小的一个。
公式表述为:
image.png
代码使用递归来实现。

  1. n = int(input())
  2. squre_nums = [i ** 2 for i in range(1, int(n ** 0.5 + 1)]
  3. def minNumSquares(k):
  4. # 如果k恰好是一个平方数,直接返回
  5. if k in squre_nums: return 1
  6. min_num = float('INF')
  7. # 遍历所有可能的情况,找到最小的值
  8. for square in square_nums:
  9. if k < square: break
  10. new_num = minNumSquare(k - square) + 1
  11. min_num = min(min_num, new_num)
  12. minNumSquares(n)

如果n较大,可能 由于过度递归,产生堆栈溢出。

思路二:动态规划

  • 要计算numSquares(10)的值, 需要计算numSquares(10 - 1), numSquares(10 - 4), numSquares(10 - 9)的值;
  • 要计算numSquares(10 - 9)的值,需要计算numSquares(10 - 1), numSquares(10 - 4)。
  • ……

可以看出,中间结果被重复计算了多次。

因可以考虑使用一个变量—备忘录dp_table,保存中间结果。

  1. class Solution:
  2. def numSquares(self, n: int) -> int:
  3. square_nums = [i ** 2 for i in range(0, int(n ** 0.5) + 1)]
  4. dp = [float('INF')] * (n + 1) # 表示组成和为i的完全平方数的个数最少是dp[i]
  5. dp[0] = 0
  6. for i in range(1, n+1):
  7. for num in square_nums:
  8. if i < num: break
  9. dp[i] = min(dp[i], dp[i - num] + 1)
  10. return dp[-1]

思路三:BFS(广度优先搜索)

广度优先搜索就是一颗N元树,一层一层地搜索。
那么节点(num,step)代表给定正整数num,需要step个完全平方数的和能组成num。
对于当前节点其子节点为:[number - ii for i in range(1, int(number*0.5)+1)]

  1. class Solution:
  2. def numSquares(self, n: int) -> int:
  3. from collections import deque
  4. queue = deque()
  5. visited = set()
  6. queue.append((n, 0))
  7. while queue:
  8. number, step = queue.popleft()
  9. targets = [number - i*i for i in range(1, int(number**0.5)+1)]
  10. for target in targets:
  11. if target == 0: return step + 1
  12. if target not in visited:
  13. queue.append((target, step + 1))
  14. visited.add(target)
  15. return 0