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.

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呢?

index0123456789101112
count0123123421233

这里想讲一下,为啥是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:dp
class 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;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值