Leetcode279. Perfect Squares

Leetcode 279. Perfect Squares

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, …) which sum to n.

Example 1:

Input: n = 12
Output: 3 
Explanation: 12 = 4 + 4 + 4.

Example 2:

Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.
解法一 动态规划
第一步 定义状态

dp[i]表示给定i的最少完全平方数的数目

第二步 初始化

初始化dp[i]为最大值。

第三步 状态转移方程
dp[0] = 0 
dp[1] = dp[0]+1 = 1
dp[2] = dp[1]+1 = 2
dp[3] = dp[2]+1 = 3
dp[4] = Min{ dp[4-1*1]+1, dp[4-2*2]+1 } 
      = Min{ dp[3]+1, dp[0]+1 } 
      = 1				
dp[5] = Min{ dp[5-1*1]+1, dp[5-2*2]+1 } 
      = Min{ dp[4]+1, dp[1]+1 } 
      = 2
						.
						.
dp[13] = Min{ dp[13-1*1]+1, dp[13-2*2]+1, dp[13-3*3]+1 } 
       = Min{ dp[12]+1, dp[9]+1, dp[4]+1 } 
       = 2
						.
						.
dp[n] = Min{ dp[n - i*i] + 1 },  n - i*i >=0 && i >= 1
public int numSquares(int n) {
    int[] dp = new int[n+1];
    Arrays.fill(dp, Integer.MAX_VALUE);
    dp[0] = 0;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j * j <= i; j++){
            dp[i] = Math.min(dp[i], dp[i - j*j] + 1);
        }
    }
    return dp[n];
}
解法二 BFS

简单地说 就是层次遍历 求第一个满足条件的节点的层次

  • 初始化队列queue=[n],访问元组visited={},初始化路径长度step = 0
  • 如果n == 0,返回0。
  • 循环条件:队列不为空
    • step += 1:循环一次意味着一层的节点遍历完毕,路径长度加一。
    • 定义当前层中的节点数 l = len(queue),遍历当前层所所有节点:
      • 取出当前队首元素 tmp
      • 遍历所有可能数i的平方数,遍历区间 [ 1 , i n t ( t m p ) + 1 ) [1,int(\sqrt{tmp})+1) [1,int(tmp )+1)
        • 定义 x = t m p − i 2 x = tmp - i^2 x=tmpi2
        • x == 0,则返回当前路径长度。
        • x not in visited,表示当前节点没有出现过,将该节点入队并加入visited数组。
  • 返回step

在这里插入图片描述

class Solution:
    def numSquares(self, n: int) -> int:
        from collections import deque
        if n == 0: return 0
        queue = deque([n])
        step = 0
        visited = set()
        while(queue):
            step+=1
            l=len(queue)
            for _ in range(l):
                tmp=queue.pop()
                for i in range(1,int(tmp**0.5)+1):
                    x=tmp-i**2
                    if(x==0):
                        return step
                    if(x not in visited):
                        queue.appendleft(x)
                        visited.add(x)
        return step
解法三 拉格朗日四平方和定理

定理:每个正整数均可表示成不超过四个整数的平方之和

重要的推论:

  1. n如果只能表示成四个整数的平方和,不能表示成更少的数的平方之和,则必定满足 n = 4 a ( 8 b + 7 ) n=4^a(8b+7) n=4a(8b+7)
  2. 如果 n%4 == 0k = n/4nk 可由相同个数的整数表示。

如何利用推论求一个正整数最少需要多少个数的平方和表示:

  1. 先判断这个数是否满足 n = 4 a ( 8 b + 7 ) n=4^a(8b+7) n=4a(8b+7),如果满足,那么这个数就至少需要 4 个数的平方和表示,即答案为4。
  2. 如果不满足,再在上面除以 4 之后的结果上暴力尝试只需要 1 个数就能表示和只需要 2 个数就能表示的情况。
    1. 如果这个数本来就是某个数的平方,那么答案就是1
    2. 如果答案是2的话,那么 n = a 2 + b 2 n=a^2+b^2 n=a2+b2,枚举 a 即可
  3. 如果还不满足,那么就只需要 3 个数就能表示

将输入的n迅速缩小。然后再判断,这个缩小后的数是否可以通过两个平方数的和或一个平方数组成,不能的话我们返回3,能的话我们返回平方数的个数。

public int numSquares(int n) {
    while (n % 4 == 0){
        n /= 4;
    }
    if ( n % 8 == 7){
        return 4;
    }
    int a = 0;
    while ( (a * a) <= n){
        int b = (int)Math.pow((n - a * a),0.5);
        if(a * a + b * b == n) {
        //如果可以 在这里返回
         if(a != 0 && b != 0) {
             return 2;
         } else{
             return 1;
        	}
    	}
    a++;
 	}
    return 3;
}

更多解法

Summary of 4 different solutions (BFS, DP, static DP and mathematics)
Static DP, C++ 12 ms, Python 172 ms, Ruby 384 ms

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值