LeetCode: 279. 完全平方数(递归思想,DP解决)

117 篇文章 0 订阅
48 篇文章 1 订阅

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

示例 1:

输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
示例 2:

输入: n = 13
输出: 2
解释: 13 = 4 + 9.

【思路】
dp题其实就是递归题,我其实很讨厌说动态规划这个名词,因为给人一种很晦涩难懂的感觉。动规的题我喜欢就把它当成纯递归的题来做,然后再运用dp思想来用数组代替函数优化就行了。所以说白了,dp就是考察递归思想!

这题,我们用递归思维来想。因为它需要将一个正整数n拆分成几个完全平方数的和嘛,所以我一下就想到了,如f(12) = min{f(12 - 1), f(12 - 4), f(12 - 9)}
那只要我们每次把小于n的完全平方数找出来不就行了吗?

所以我用一个tag数组,预处理,所有小于n的完全平方数的tag值全被我标记成了1.利用这种索引思想,就可以很快的判断一个数是不是完全平方数了!

递归程序就可以很轻松的写出了:

class Solution {
public:
    int tag[10000];
    
    void findnum(int n)
    {
        for(int i = 1;i * i <= n;i++)
        {
            tag[i * i] = 1;             //标记完全平方数
        }
    }
    
    int f(int n)
    {
        if(n == 1)                      //边界
        {
            return 1;
        }
        else
        {
            if(tag[n] == 1)             //自己就是完全平方数
                return 1;
            else
            {
                int min_res = 0x3f3f3f3f;
                for(int i = 1;i * i <= n;i++)       //找出所有比自己小的完全平方数,值为i * i
                {
                    if(tag[i * i] == 1)             //如果是完全平方数
                    {
                        int res = f(n - i * i);     //根据递归式
                        if(res + 1 < min_res)
                            min_res = res + 1;   
                    }
                }
                return min_res;
            }
        }
    }
    
    int numSquares(int n) {
        findnum(n);                     //预处理,找出所有的完全平方数
        return f(n);
    }
};

好,现在我们已经写好了递归程序了,这个程序必然是超时的,我们只需要利用dp数组,把中间算过的结果存下来避免重复计算就好了,这就是动态规划加速的思想。那我们对着这个递归程序,其实动规程序也可以很轻松的写出来了

AC代码:

class Solution {
public:
    int tag[10000];
    int dp[10000];
    
    void findnum(int n)
    {
        for(int i = 1;i * i <= n;i++)
        {
            tag[i * i] = 1;             //标记完全平方数
        }
    }
    
    int DP(int n)
    {
        dp[1] = 1;                      //对应递归中的边界
        for(int i = 2;i <= n;i++)
        {
            if(tag[i] == 1)
            {
                dp[i] = 1;
            }
            else
            {
                int min_res = 0x3f3f3f3f;
                for(int j = 1;j * j <= i;j++)
                {
                    if(tag[j * j] == 1)
                    {
                        int res = dp[i - j * j];
                        if(res + 1 < min_res)
                            min_res = res + 1;
                    }
                }
                dp[i] = min_res;
            }
        }
        return dp[n];
    }
    
    int numSquares(int n) {
        findnum(n);                     //预处理,找出所有的完全平方数
        return DP(n);
    }
};

所以真的就是这样,动规的题,就把它当作纯递归的题来思考,dp只是一种优化手段而已。这样做,就不会再觉得动规的题很难很难了。如果说是因为想不到状态转移方程而不会做这道dp题,那只能说递归思维还不够,应该多练递归的题

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
给定一个整数数组 nums 和一个目标值 target,要求在数组中找出两个数的和等于目标值,并返回这两个数的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个数的和是否等于目标值。如果等于目标值,则返回这两个数的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个数的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的数的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个数的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值