问题:完美平方

有关完美平方的几个解题思路

问题描述:

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

问题示例:

//给出 n = 12, 返回 3 因为 12 = 4 + 4 + 4。
//给出 n = 13, 返回 2 因为 13 = 4 + 9。

思路、可考虑的算法分析:

1、 贪心算法
贪婪算法(贪心算法)是指在对问题进行求解时,在每一步选择中都采取最好或者最优(即最有利)的选择,从而希望能够导致结果是最好或者最优的算法算法所得到的结果不一定是最优的结果(有时候会是最优解),但是都是相对近似(接近)最优解的结果以上面的问题问题,若使用贪心算法求出的结果,有时候不一定是最优的结果,因为有可能成本问题不是最好的,但是它是相对接近最优解的。
对于本题,贪心算法明显是不适宜的,以12为例,如果使用贪心,则12 = 9 + 1 + 1 + 1,返回值为4,而不是样例中的3。
然后,考虑用贪心算法得到的数字为上限,比如说12,上限为4,则如果12/1,12/2,12/3中有能整除且整除结果为完全平方数的,则为正确结果。
该想法的代码如下(java):

   public int numSquares(int n) {
        int ret = numSqrt(n);
        for (int i = 1; i < ret; i++) {
            if (n / i * i == n) {
                if (isSqare(n / i)) {
                    return i;
                }
            } else {
                continue;
            }
        }
        return ret;
    }

    private static boolean isSqare(int n) {
        int temp = (int) Math.sqrt(n);
        return temp * temp == n;
    }

    private static int numSqrt(int n) {
        int temp = (int) Math.sqrt(n);
        if (temp * temp == n) {
            return 1;
        } else {
            return 1 + numSqrt(n - temp * temp);
        }

事实证明想的太过简单了,当测试到1684时,系统给出的答案是2,而不是按照以上代码获得的4。那么问题出在哪呢?
还是找个实例,61 = 25 + 36, 结果应该为2,然后按照上述算法上限为5,且61是个素数,无法整除2,3,4,结果就会返回5,因此,这个算法也是有问题的。
2、 递归
用递归的方法,每次减去一个小于等于n的最大完全平方数,并将得到的差赋给n,用一个函数进行递归,每次调用记录数+1,直到得到的差为0。
例如13 - 9 = ,4,记录数为1;4 - 4 = 0,记录数为2;函数最后一级返回2.但是问题在于12 = 4 + 4 +4,如果用这种思路得到的将是12 = 9 + 1 + 1 + 1,即返回4.因此这个方法不可行。
3、 Lagrange 四平方和定理
a.任何正整数都可以拆分成不超过4个数的平方和 —> 答案只可能是1,2,3,4
b.如果一个数最少可以拆成4个数的平方和,则这个数还满足 n = (4^a)*(8b+7) —> 因此可以先看这个数是否满足上述公式,如果不满足,答案就是1,2,3了
c.如果这个数本来就是某个数的平方,那么答案就是1,否则答案就只剩2,3了
d.如果答案是2,即n=a2+b2,那么我们可以枚举a,来验证,如果验证通过则答案是2
e.只能是3
代码如下(python):

def numSquares(n):
    while n%4==0:
        n//=4
    if n%8==7:   #因为7 最少由  1,2,4  三个数组成
        return 4

    for i in range(n+1):
        temp=i*i
        if temp<=n:
            if int((n-temp)**0.5)**2 +temp==n:
                return 1 +(0 if temp ==0 else 1)
        else:
            break
    return 3
s =numSquares(99)
print(s)

4、 DFS(Depth-First-Search)深度优先搜索算法
可不可以在判断完美平方数的同时将该数由哪些完全平方数组成呢?
代码如下:

from  collections  import  deque  as  dq
import  numpy as np
import  math
import pandas as pd
def  perfect(n):
    a=np.repeat(-1,n+1)
    a[n]=n
    q=dq()
    q.append(n)
    sq=pd.Series(range(1,math.floor(math.sqrt(n))*2+1,2)).cumsum().values    
    while q.__len__()>0  and  a[0]<0:
        x=q.popleft()
        l=x-sq
        l=l[l>=0]
        l=l[a[l]==-1]
        q.extend(l)
        a[l]=x
    i=0
    j=a[i]
    while  i<n:
       print(j-i,end=" ")
       i=j
       j=a[i]

DFS(Depth-First-Search)深度优先搜索算法,是搜索算法的一种。是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点 。
代码具体实现思路及举例说明(取n = 14)如下:
(1).创建一个包含n+1个数的行矩阵,前n个数赋值为-1表示未探索,第n+1个数赋值为n

a=np.repeat(-1,n+1)
a[n]=n

得到a = [-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 14]

(2).创建一个duque对象,方便使用队列操作,将初始值n存为队列的第一个元素

q=dq()
q.append(n)

得到q = deque([13]),存取待访问路径入口

(3).获取从1到n范围内的所有完全平方数存入行矩阵sq,作减数待用

sq=pd.Series(range(1,math.floor(math.sqrt(n))*2+1,2)).cumsum().values
#math.floor(x)取小于等于x的最大整数
#series是一个一维数组,Pandas会默然用0到n-1来作为series的index,第一列为index,第二列为值
#cumsum求累计次数
#values取值
#最终得到的是小于等于n的所有完全平方数

得到sq = [1 4 9]

(4).开始寻求最短路径,每次从队头中弹出一个数,初始值为原数n,用弹出的数减去得到的矩阵sq得到余数矩阵l,此时已经走出路径的第一步,会有m种可能(m为sq行维度)循环条件为队列中还有元素且存储访问标志的a的第一个元素小于零

while q.__len__()>0  and  a[0]<0:
    x=q.popleft()
    l=x-sq

然后判断余数矩阵中的数是否大于等于零,余数为负数的路径是不合法的,在余数矩阵中会被清除;
再判断所得余数是否被探索过或者是否已存入待探索矩阵,不符合条件的被会清除,不必重复探索;
经过上述两轮更新,矩阵l中剩下的即合法且还未探索过的余数。

l=l[l>=0]
l=l[a[l]==-1]

再将l中的余数送入队列q中,进行下一轮探索;将余数作为索引,此轮被减数x作为值,更新访问标志矩阵

q.extend(l)
a[l]=x

直到队列中元素都被探索完了或者有某个余数变为0,循环终止,代表最短路径搜索完毕
第一次循环:

x = 14
q = deque([])
l = [13 10 5]
l = [13 10 5]#第一次更新
l = [13 10 5]#第二次更新
q = deque([13 10 5])
a = [-1 -1 -1 -1 -1 14 -1 -1 -1 -1 14 -1 -1 14 14]
#索引为5,10,13的访问标志被更新为14,表示该处值由14作为被减数减去sq得到

循环条件成立,循环继续

第二次循环:

x = 13
q = deque([10, 5])
l = [12 9 4]
l = [12 9 4]#第一次更新
l = [12 9 4]#第二次更新
q = deque([10, 5, 12, 9, 4])
a = [-1 -1 -1 -1 13 14 -1 -1 -1 13 14 -1 13 14 14]
#索引为的4,9,12的访问标志被更新为13,表示该处值由13作为被减数减去sq得到

循环条件成立,循环继续

第三次循环:

x = 10
q = deque([5, 12, 9, 4])
l = [9 6 1]
l = [9 6 1]#第一次更新
l = [6 1]#第二次更新,索引为9处已有值,去掉元素9
q = deque([5, 12, 9, 4, 6, 1])
a = [-1 10 -1 -1 13 14 10 -1 -1 13 14 -1 13 14 14]
#索引为的1,6的访问标志被更新为10,表示该处值由10作为被减数减去sq得到

循环条件成立,循环继续

第四次循环:

x = 5
q = deque([12, 9, 4, 6, 1])
l = [ 4 1 -4]
l = [ 4 1]#第一次更新,去掉-4
l = []#第二次更新,1,4都已存入q中待探索
q = deque([12, 9, 4, 6, 1])
a = [-1 10 -1 -1 13 14 10 -1 -1 13 14 -1 13 14 14]
#状态不变

循环条件成立,循环继续

第五次循环:

x = 12
q = deque([9, 4, 6, 1])
l = [11 8 3]
l = [11 8 3]#第一次更新
l = [11 8 3]#第二次更新
q = deque([9, 4, 6, 1, 11, 8, 3])
a = [-1 10 -1 12 13 14 10 -1 12 13 14 12 13 14 14]
#索引为3,8,11的访问标志被更新为12,表示该处值由12作为被减数减去sq得到

循环条件成立,循环继续

第六次循环:

x = 9
q = deque([4, 6, 1, 11, 8, 3])
l = [8 5 0]
l = [8 5 0]#第一次更新
l = [0]#第二次更新,5已探索过,8已存入q中待探索
q = deque([4, 6, 1, 11, 8, 3, 0])
a = [ 9 10 -1 12 13 14 10 -1 12 13 14 12 13 14 14]
#索引为0的访问标志被更新为9,表示该处值由9作为被减数减去sq得到

循环条件不成立,循环结束

(5).接下来便是按照探索路径反着回去取出该最短路径
余数为0时a矩阵的值为最后一段,该值作为最后一段的被减数,减去它本身得到零,为最后一段路径的完全平方数,输出该路径;
同时,该值又是上一路径的索引,可以取出上一路径的被减数,与其相减即可得到上一路径的一个完全平方数,输出该路径;
以此类推,取到第一段路径的数为n,路径全都被取出。

i=0
j=a[i]
while  i<n:
   print(j-i,end=" ")
   i=j
   j=a[i]

i = 0
j = 9
第一次循环:
输出9 - 0 = 9 #差值为本路径的值
i = 9 #上一路径的索引,下一路径的被减数
j = 13 #上一路径的被减数
循环条件成立,循环继续

第二次循环:
输出13 - 9 = 4
i = 13
j = 14
循环条件成立,循环继续

第三次循环:
输出14 - 13 = 1
i = 14
j = 14
循环条件不成立,循环结束
最终输出的值为9 4 1,即13 = 9 + 4 + 1为最短路径

5、 动态规划
要求n的完美平方数p(n),
假如n的完美平方式子中包含1,则p(n) = p(n-1) + 1;
假如n的完美平方式子中包含4,则p(n) = p(n-4) + 1;
假如n的完美平方式子中包含9,则p(n) = p(n-9) + 1;
……
因此,要求p(n),只需先求p(n-1), p(n-4), p(n-9) …… 再找出它们中的最小值,再加一。
代码如下(python):

class Solution(object):
    def numSquares(self, n):
        """
        :type n: int
        :rtype: int
        """
        # 用上限n初始化结果数组
        nums = [i for i in range(n+1)]
        # 从1开始填充结果数组
        for i in range(1, n+1):
            j=1
            # 所有可能包含的平方数j
            while j*j <= i:
                nums[i] = min(nums[i], nums[i-j*j]+1)
                j += 1
        return nums[n]

动态规划(dynamic programming,简称dp)是工程中非常重要的解决问题的思想,从我们在工程中地图软件上应用的最短路径问题,再在生活中的在淘宝上如何凑单以便利用满减券来最大程度地达到我们合理薅羊毛的目的,很多时候都能看到它的身影。不过动态规划对初学者来说确实比较难,dp状态,状态转移方程让人摸不着头脑,网上很多人也反馈不太好学,其实就像我们之前学递归那样,任何算法的学习都是有它的规律和套路的,只要掌握好它的规律及解题的套路,再加上大量的习题练习,相信掌握它不是什么难事,本文将会用比较浅显易懂地讲解来帮助大家掌握动态规划这一在工程中非常重要的思想,相信看完后,动态规划的解题套路一定能手到擒来(文章有点长,建议先收藏再看,看完后一定会对动态规划的认知上升到一个台阶!)
动态规划详解,参考:
https://blog.csdn.net/Daniel_Wu1/article/details/105401988

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个比较复杂的问题,需要用到一些数学知识和算法。首先,我们可以枚举a1、a2和a3的所有可能取值,然后判断是否满足给定的条件。如果满足条件,则计算它们的和,并与之前的结果比较,保留最大值。 具体来说,我们可以用三重循环来枚举a1、a2和a3。对于每组取值,我们可以分别判断它们是否满足条件,具体如下: 1. 判断a1+a2是否为质数:可以先计算它们的和sum,然后从2到sum-1枚举所有可能的因子,判断是否存在满足条件的因子即可。如果存在,则说明sum不是质数。 2. 判断4a2+a3是否为完全平方数:可以先计算它们的和sum,然后求出sum的平方根sqrt_sum,判断sqrt_sum的整数部分是否等于4a2+a3即可。如果不等于,则说明sum不是完全平方数。 3. 判断a1+2a2+4a3是否为完美数:可以先计算它们的和sum,然后枚举所有小于sum的正整数,计算它们是否为sum的因子,并累加起来。如果累加和等于sum,则说明sum是完美数。 4. 判断a1+2a2+a3是否为k的倍数:可以先计算它们的和sum,然后判断sum是否为k的倍数。 最后,我们可以将满足条件的sum和对应的a1、a2、a3保存下来,最终输出它们的和最大值对应的a1、a2、a3即可。 需要注意的是,对于较大的n,直接枚举可能的a1、a2和a3会非常耗时,因此需要考虑一些优化方法,例如剪枝、缩小枚举范围等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值