力扣【279】完全平方数

题目:

给定正整数 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

题解:

说法一:

转态转移方程的得出 f(i)=f(i-j*j)+1 假设i为组成平方数的最少个数,最后一个数值为 j*j, 由于最优子结构(无法再进行拆分,可以想象成5=1+2*2 ) 得出 f(i)=f(i-j*j)+1 

什么叫最优子结构?

“知乎用户:我的理解是要求的目标函数一般都具有“单调性”,诸如绝对值,模值这种的目标函数就不具有最优子结构。”

动态规划的最优子结构问题,有什么样的问题它不满足最优子结构? - 王赟 Maigo的回答 - 知乎 https://www.zhihu.com/question/52165201/answer/288025858

关于最优子结构、无后效性和重复子问题的定义: -CSDN的回答:https://blog.csdn.net/qq_25800311/article/details/90635979

知乎回答:“我的理解是要求的目标函数一般都具有“单调性”,诸如绝对值,模值这种的目标函数就不具有最优子结构。”

说法二:

题目要求将一个给定整数拆分成若干完全平方的和,要求用到的这些完全平方数最少。

拿到题目后第一个思路可以很快想到,就是尝试通过贪心的策略解题,每次找能匹配上的最大完全平方。

比如对于例子 n = 12 我先找 3*3 = 9 然后再找 3个1。

发现对于给定的示例就是不符合的,所以这个思路作罢。

第二个思路可以想到通过广度优先遍历的策略进行解题,就是每次拿一遍小于n的完全平方数,去凑n,然后一点一点去逼近n,

先凑成n时所需的个数就是最少个数。这个方法是可行的

第三个思路就是动态规划的想法,产生这个想法的原因如下:

如果我要凑一个数 n , 假设我选择的最大完全平方为m 那么就有 m + diff = n, 要想使凑n所用到的完全平方个数最少,

下一步就转化为求 凑diff所用到的完全平方数最少,然后依次递归下去。这就是同时具备重叠子问题最优子结构

那么显然我们可以用动态规划的思路去做,上面描述的过程可以很简单的转化为如下的式子:

dp[n] = 1 + dp[n - m*m]; 其中m是满足n用到完全平方个数最少时,所用的某一个完全平方的根。

分析到这里我们下一步就要去求这个m,这里我们可以通过一个循环的方式,去找最小的dp[n - m*m]即可

代码如下👇👇👇,时间复杂度为O(N^(1.5));

import java.util.*;

class Solution {
    public int numSquares(int n) {
        // 默认初始化值都为0
        int[] dp = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            // 最坏的情况就是每次+1
            dp[i] = i;
            for (int j = 1; i - j * j >= 0; j++) {
                // 动态转移方程
                // 假如i=12,就是求问题j=2时,dp[i-j*j]最小,还是j=3时,dp[i-j*j]最小。
                // 1就代表2*2或者3*3已经确定了,必须有这一个最大值,然后求dp[diff=i-j*j]的最小值。
                // 重叠子问题是求min(dp[j])和min(dp[i-j*j]),它俩是一个问题
                // 最优子结构是每一个问题都是dp[i-j*j]+1,“+1”代表有一个值j*j已经确定了,求其余的值的最小值。
                dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
            }
        }
        return dp[n];
    }
}

/**
 * @author wyl
 */
public class Main {
    public static void main(String[] args) {
        Solution s = new Solution();
        int n = 12;
        int res = s.numSquares(n);
        System.out.println("输出结果:" + res);
    }
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值