Leetcode279: 完全平方数(medium, BFS)

目录

1. 问题描述

2. 解题分析

3. 代码及测试

4. 后记 


1. 问题描述

        给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数(可以重复)的个数最少。给你一个整数 n ,返回和为 n 的完全平方数的最少数量。

        完全平方数是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

示例 1:

输入:n = 12,输出:3

解释:12 = 4 + 4 + 4

示例 2:

输入:n = 13, 输出:2

解释:13 = 4 + 9

提示:1 <= n <= 10^4

2. 解题分析

        本题可以转化为图搜索中的最短路径问题,因此可以用广度优先搜索算法来解决。

        举个例子,令n = 30, m=floor(sqrt(n)) = 5. 则构造n的完全平方数的和,可以使用模块(即数字)为1~5。从中任选一个k使用得到(n=30)的邻节点(n-k*k)。针对每个节点都执行同样操作,直到最后到达值为0的节点。如下图所示:

        Edge上的数字表示要用于构成完全平方数和的数(其实是其平方),对应的子节点的值等于父节点的值减去该数的平方。

        广度优先搜索的三个基本要素:

  1. 队列管理
  2. 节点的表示以及邻节点的遍历
  3. 已访问节点的管理

        在本问题中,邻节点即从当前节点出发,将每个可用的数(的平方)用一次(即减去)后能到达的下一个值。可以以递推关系式表示如下:

3. 代码及测试

import sys
import time
import datetime
import math
import random
from   typing import List
# from   queue import Queue
from   collections import deque
import itertools as it
from   math import sqrt, floor, ceil
import numpy 

class Solution:

    def numSquares_bfs_set(self, n: int) -> int:
        queue = deque()
        queue.append((n,0))
        visited = set([n])
        
        while queue:
            vertex = queue.popleft()
            for k in range(1,floor(sqrt(vertex[0])+1)):
                nxtNum = vertex[0] - k*k           
                if nxtNum==0:                   
                    return vertex[1]+1
                    
                elif nxtNum not in visited:
                    queue.append((nxtNum,vertex[1]+1))
                    visited.add(nxtNum)
                                        
        return -1

if __name__ == '__main__':        
    
    sln = Solution()
    
    # n = 4696
    n = 9999

    tStart = time.time()
    nums = sln.numSquares_bfs_set(n)
    tCost = time.time() - tStart
    print('numSquares_bfs_set({0}) = {1}, tCost = {2}(sec)'.format(n,nums,tCost))

        函数最后返回"-1" ,其实意思是正常不应该到到达这里。到达这里意味着程序存在错误。

        另外需要注意的是,deque是双向队列,它的append()是和popleft()相对应的。另一对对应的方法是appendleft()和pop()。

        提交结果: 

4. 后记 

        (1) 本题一开始提交超时失败了。原因查明了是因为使用list来实现visited,是python list用于查询的性能太差导致超时了。改用set实现后就OK。比较了一下,用set实现的查询效率比list实现的查询效率毛估估高两个数量级。另外,还可以用dict来实现visited,dict的查询效率与set相当。后面有时间要做一次三者的查询效率对比实验。

        (2) 如第2节中的递推表达式所示,本题也可以用动态规划的策略来解决。而且有不止一种递推关系。这个后面再来补课。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨牛慢耕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值