279. Perfect Squares

来源:互联网 发布:知乎日本反战同盟 编辑:程序博客网 时间:2024/06/06 04:56

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.

s思路;
1. 想了半天,觉得应该用dp.比如:先建立一个vector< int> dp(n+1).例如n=12,则:先用while(i*i<=n),把所有平方数的位置填1,所以就有[1,1,0,0,1,0,0,0,0,1,0,0,0],即:dp[0]=dp[1]=dp[4]=dp[9]=1,因此12-9,等于3,发现3的位置为0,表示还没有计算,因此计算3,3之前只有1是完全平方,所以找3-1=2的组成,发现2也没计算,所以找2,2之前也只有一个完全平方1,所以2-1=1,1就是完全平方数,所以可以把2和3的组成找到,即:2=1+1,3=1+1+1。这个思路有点像dfs。
2. 参考https://discuss.leetcode.com/topic/24255/summary-of-4-different-solutions-bfs-dp-static-dp-and-mathematics/2
3. 根据上面的链接,这道题还可以用bfs,更妙!先把完全平方数找出来,例如上面n=12,则1,4,9就是完全平方数,然后判断n是否就是完全平方数,不是的话,那么就把这3个数放入queue,然后每次取出的时候和1,4,9相加,看相加的结果是否访问过,如果没有访问过,那么就给这个位置的次数就加1。为什么是bfs呢?

index 0 1 2 3 4 5 6 7 8 9 10 11 12 count 0 1 2 3 1 2 3 4 2 1 2 3 3

这里想讲一下,为啥是bfs?很有意思。所有完全平方数的count都是1,也就是需要1个完全平方就可以组成这个数;由于这些数都是完全平方数,那么就是bfs的一个层次的;同样的,所有的count=2的位置,例如:2,5,8,10则是另一个层次的,这也是为什么用bfs来做的原因,而且2,5,8,10可以由第一个层次的1,4,9和基本组成单元1,4,9求和得到。这里更有意思的是,3个数和3个数两两相加本来应该有9种结果,可是这里只有4种结果。为什么?这就是迷惑人的地方了,或者不容易看到的地方,需要思考。比如1+4==4+1重复了,所以看不见,或者换一种说法,看到5,不要以为就是一个5,他其实包含有多个5,只是这种表达方式看不出来,但真相就是这个5是多个5而不是一个5;还有一类是9+9>12了,这种结果也不需要,因为超出我们关心的范围了,因此第二层就只有4个能看见的数;继续下去,我们得到更深的层次。注意每个层次都有相同的count值,这也是我们划分层次的依据。下一个层次是通过把2,5,8,10和基本组成单元1,4,9求和得到,即:3,6,11,12.还是刚才的原因,本来应该有12个求和结果,但是因为重复或超过12的原因,而保留下来4个,此时我们就得到了12的层次数就是组成12的完全平方数的个数了。
4.用bfs比用dp快,用dp复杂度是o(n*sqrt(n)),但dp有很多重复计算,bfs每次都要排除重复和越界的,不停的在剪枝。这道题给我的启发是,先画出一些点,看有没有什么规律,而且还有数学方法证明所有数可以用最多4个平方数之和表示,因此,有大量的数是1个平方数,有大量的数是2个平方数,还有大量数是3个平方数,一个平方数就是下边界,即:基本情况,本身就是完全平方数,这应该是我们问题的起点。找到起点之后,再看问题如何evolve:如何从1个变成2个?这个也简单,就是把所有一个平方数和一个平方数分别加,就得到2个的情况,然后从2个得到3个,就用2个的平方数和一个的平方数分别加。
5. 自己看到题,也尝试这么思考,想先找到问题的边界,或元问题,或基本问题,这里就是先找到所有本身就是完全平方的数,以此为起点,找到并标记所有两个完全平方和的数,再找三个或四个,也就是要看到元问题和最后要求的问题之间的差别是哪儿,如果不在一个层次上,就可以用bfs去一级一级的深入。能想到这个答案的人,都是牛人啊!
6. 这道题的终极解法,就是数学了!因为最后答案就是1-4,范围很小,所以很容易就猜到用数学公式很容易搞定,数学方法采用top-down的系统解法,不像我们用dp还是bfs,本质还是bottom-up:先找到元问题(起点),再找到每一步如何走(递推关系),最后就一定可以走向答案!通过这道题,又加深了对bfs和dp,以及数学的相互关系!大写的赞!妙!

//方法1:dpclass Solution {public:    int numSquares(int n) {        //        vector<int> dp(n+1,INT_MAX);        dp[0]=0;        for(int i=1;i<=n;i++){            for(int j=1;j*j<=i;j++){                dp[i]=min(dp[i],dp[i-j*j]+1);                }            }        return dp[n];    }};//方法2:bfs 推荐!从一堆数据中找到元问题,发现本质就是一个bfs//只有发现是bfs,才能轻松写出来,立场决定眼界呀!class Solution {public:    int numSquares(int n) {        //        vector<bool> visited(n+1,0);        vector<int> squarenum;        for(int i=1;i*i<=n;i++){            int j=i*i;            if(j==n) return 1;            squarenum.push_back(j);            visited[j]=1;           }        queue<int> bfs;        for(int k:squarenum)            bfs.push(k);        int count=1;        while(!bfs.empty()){            count++;            int sz=bfs.size();            for(int j=0;j<sz;j++){                int cur=bfs.front();                for(int i=0;i<squarenum.size();i++){                    int tmp=cur+squarenum[i];                    if(tmp==n) return count;                    if(tmp>n) break;                    if(tmp<n&&(visited[tmp]==0)){                        visited[tmp]=1;                        bfs.push(tmp);                    }                   }                bfs.pop();            }        }        return 0;    }};
0 0