【leetcode】【medium】279. Perfect Squares

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.

题目链接:https://leetcode-cn.com/problems/perfect-squares/

法一:dp

比较常规的思路,时间复杂度O(nlogn)。

对于a>b: f(a) = f(a-i^2)+1,其中i<=\sqrt{a}

能想通这个,就能写出状态转移方程:

dp[n]=dp[n-1^2]+1, i\leq \sqrt{n}

dp[1]=1

class Solution {
public:
    int numSquares(int n) {
        if(n<=1) return n;
        int record[n+1];
        record[1] = 1;
        for(int i=2; i<=n; ++i){
            record[i] = i;
            for(int j = sqrt(i); j>0; --j){
                int tmp = i-j*j;
                if(tmp==0){
                    record[i] = 1;
                    break;
                }else{
                    record[i] = min(record[i], record[i-j*j]+1);
                }
            }
        }
        return record[n];
    }
};

法二:转为图的BFS问题

此方法是看分享理解的,在计算上和dp的思路差不多,区别在于:dp从小推到大,图问题从目标出发向前推需要计算的数;以及编程思路是从BFS的方法出发。

数字可以看作图上的点,连边的条件是两个数字之间只差一个平方数。因此问题转为无权图上寻找两点之间的最短路径。

用队列存储需要下面需要计算的节点。

class Solution {
public:
    int numSquares(int n) {
        if(n<=1) return n;
        queue< pair<int,int> > record;
        record.push(make_pair(n,0));
        bool visit[n+1] = {false};
        while(!record.empty()){
            auto tmp = record.front();
            record.pop();
            for(int i=sqrt(tmp.first); i>=0; --i){
                int sub = tmp.first - i*i;
                if(sub==0) return tmp.second+1;
                if(!visit[sub]){
                    record.push(make_pair(sub, tmp.second+1));
                    visit[sub] = true;
                }
            }
        }
        return -1;
    }
};

法三:数学结论

拉格朗日四个方形定理:

任何一个数,都可以由小于等于4个的完全平方数相加得到。

推论:当n满足如下公式时,才只能由4个完全平方数得到, a和b为某个整数:n = 4^a (8b + 7)

而1个平方数可以直接开放得到,2个平方数可以暴力迭代得到,3个平方数则是除去这3种情况之外剩余的数。

class Solution {
public:
    int numSquares(int n) {
        if(n<=1) return n;
        if(n-(pow(int(sqrt(n)),2))==0) return 1;
        for(int i=sqrt(n); i>=1; --i){
            int tmp = n-i*i;
            if(tmp-(pow(int(sqrt(tmp)),2))==0) return 2;
        }
        for(int i=0; n>0; ++i, n/=4){
            if((n-7)%8==0) return 4;
            if(n%4) break;
        }
        return 3;
    }
};
发布了127 篇原创文章 · 获赞 2 · 访问量 3758

猜你喜欢

转载自blog.csdn.net/lemonade13/article/details/104317477