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.

For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.


求一个数最少能分成几个完全平方数的和。

1,4,9,16,25,36。。。。这些数肯定就是他们自己,那不是完全平方数的数呢?

比如18,如何求18呢?如何分辨18=9+9,而不是16+2?使用检阅所有数肯定会做很多无用功。

18分成多个平方数的和,分成9、9或4、4、9、1,或分成16、1、1,或者其他平方数的组合,

无论哪一种组合其中必有一个小于18的平方数,这个数再加上剩下的数就是18。我们只要分辨所有小于18的平方数加上其对应的剩下的数,求这些值中的最小值。

剩下的数也一定是平方数加一个剩下数。如此将问题变小,使用动态规划思想,反向积累。

int numSquares(int n) {
        vector<int> dp(n+1,1);
        for(int i=1; i<=n; i++){
            dp[i] = i;
            if(sqrt(i)*sqrt(i)==i){
                dp[i]=1;
                continue;
            }
            for(int j=1; j*j<=i; j++){
                dp[i] = min(dp[i], dp[i-j*j] + 1);
            }
        }
        return dp[n];
    }

然而这样在leetcode上通过不了,特别是12之后会出现错误(正确值为3,得到值为2),但在我的sublime(使用g++)中却是正确的,至少12这个数是正确的。


在别的答案里有java类似我的答案的也对了,不知道的为啥出错。

同时看了别人的c++答案,发现一个新大陆,static dp[],这是我第一次看到有人用static dp[]。

class Solution {
public:
    int numSquares(int n) {
        static vector<int> dp{0};
        while (dp.size() <= n) {
            int m = dp.size(), squares = INT_MAX;
            for (int i=1; i*i<=m; ++i)
                squares = min(squares, dp[m-i*i] + 1);
            dp.push_back(squares);
        }
        return dp[n];
    }
};
其实两个算法思路一样。另,本着没必要不装逼,能不用就不用的原则,结果我试了一下删去static,也能运行出正确答案,干嘛还要static,这里又没有过多的传递问题,也不会被偷吃掉。

然后就是使用数学的方法解决问题了,这种方法很难想的到,但是效率很快。我不解释了,你们自己看吧,这种数学方法不像使用dp,dp能解决一类问题,而特殊数学方法则解决问题的宽度就比较窄,需要对数学知识有个很宽的了解才能熟练解决对应的问题。而且一般一个数学方法只解决一个问题。所以我觉得了解一下就好了,不用过多投入,除非你是数学专业。这里将具有普遍算法性质的和针对性质的数学方法分开了,不要和我乱撕逼,我会不理你的。

class Solution {
public:
    int numSquares(int n) {
        while (n % 4 == 0)
            n /= 4;
        if (n % 8 == 7)
            return 4;
        for (int a=0; a*a<=n; ++a) {
            int b = sqrt(n - a*a);
            if (a*a + b*b == n)
                return 1 + !!a;
        }
        return 3;
    }
};

算法分析

大家自己看吧,算法都挺简单,但是原理挺难的,这个和数论有关吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值