leetcode-279. Perfect Squares

来源:互联网 发布:设计师必备软件 编辑:程序博客网 时间:2024/06/07 08:31

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.

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

该问题要求求出能组成一个整数的最小的平方数是多少,即12=4+4+4,那么它的最小的平方数为3. 13=4+9,那么它的最小的平方数为2.
该问题有以下四种解法:(4种解法均来自https://discuss.leetcode.com/topic/24255/summary-of-4-different-solutions-bfs-dp-static-dp-and-mathematics)

解法一:数学解法

主要运用的是拉格朗日4平方数定理:
即任何一个自然数都可以由不超过4个的平方数组成:
该定理的维基百科和证明如下:
https://en.wikipedia.org/wiki/Lagrange%27s_four-square_theorem
https://www.alpertron.com.ar/4SQUARES.HTM

于是,该完美平方数的证明可以转化为分别是否由1个,2个,3个,4个平方数组成的。

然后根据拉格朗日定理可知,只要一个数i满足
i=4k(8m7)
k,m均为>=0的整数,说明i最小由4个平方数组成

如果一个数是平方数,则说明一个数由一个平方数组成

判断一个数是否由两个组成,先减去i的最大平方数,再判断剩下的数是否是平方数。

最终代码如下:

class Solution {private:    int is_square(int n)    {        int SqNum=(int)(sqrt(n));        return (SqNum*SqNum==n);    }public:    //根据拉格兰日的定理,每个整数都可以由4个平方数组成    int numSquares(int n) {    //是否能由一个数字组成    if(is_square(n))    {        return 1;    }    //是否能由三个数字组成    //根据拉格兰日定理可知    //只要n=4^(k*(8*m-7))    //先判断是否能被4整除    while((n&3)==0)   //n%4==0    {        n>>=2;    }    if((n&7)==7)    //m%8==7    {        return 4;    }    //判断是否能由两个数字组成    int sqrt_n=(int)(sqrt(n));    for(int i=1;i<=sqrt_n;++i)    {        if(is_square(n-i*i))        {            return 2;        }    }    return 3;    }};

解法二:动态规划

直接说明递推公式吧,递推公式如下

result[0]=0;result[n]=min(result[n],result[n-j*j]+1);

每个一个数都可以由一个数加上一个平方数构成,由此关系可以构成一个递推公式

代码如下:

class Solution {public:    int numSquares(int n) {    if(n<=0)        return 0;    vector<int> result(n+1,10000);    result[0]=0;    for(int i=1;i<n+1;++i)    {        for(int j=1;j*j<=i;++j)        {            result[i]=min(result[i],result[i-j*j]+1);        }    }    return result[n];    }};

解法三:静态动态规划

即通过创建static vector,OJ的多次函数调用来对一个动态规划的表做到多次利用,即numSquares(100)其实只要调用numSquares(10)的表就好,不需要创建新的表,相当于利用了OJ的小trick。

代码如下:

class Solution {public:    int numSquares(int n) {    if(n<=0)        return 0;    static vector<int> result({0});    while(result.size()<=n)    {        int m=result.size();        int minResult=INT_MAX;        for(int i=1;i*i<=m;++i)        {            minResult=min(minResult,result[m-i*i]+1);        }        result.push_back(minResult);    }    return result[n];    }};

解法四:广度优先搜索

把小于等于n的平方看成一个图的结点,那么节点与节点之间是否相连取决于是否满足SquareNumber+i=j,i为原数字,j为结果数字,SquareNumber表示为平方数,同时维护一个数组cntSquareNumber,表示该结点是由几个平方数组成,这样利用广度优先搜索该图的平方结点直到SquareNumber+i=n,返回最终的最小的平方数,得到结果

class Solution {public:    int numSquares(int n) {    vector<int> cntNumber;    //说明数字由几个平方数组成的数组    vector<int> cntSquareNumber(n);    for(int i=1;i*i<=n;++i)    {        cntNumber.push_back(i*i);        cntSquareNumber[i*i-1]=1;    }    if(cntNumber.back()==n)    {        return 1;    }    queue<int> resultQ;    for(auto& i:cntNumber)    {        resultQ.push(i);    }    int result=1;    while(!resultQ.empty())    {        result++;        int size=resultQ.size();        for(int i=0;i<size;++i)        {            int temp=resultQ.front();            for(auto& j:cntNumber)            {                if(temp+j==n)                {                    return result;                }                else if((temp+j<n) && (cntSquareNumber[temp+j-1]==0))                {                    cntSquareNumber[temp+j-1]=result;                    resultQ.push(temp+j);                }                else if(temp+j>n)                {                    break;                }            }            resultQ.pop();        }    }    return 0;    }};
原创粉丝点击