LeetCode 279. Perfect Squares

189 篇文章 0 订阅
162 篇文章 0 订阅

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.

 

 

 

注:

针对这道题,是不可能没有解的。因为1本身就是一个完全平方数,任何一个数都可以表示为1+x。

很多人拿到这个题的时候,直觉的解法就是使用贪心算法。换句话说,反正每一个数n都可以被n个1表示,那么我想用最小的完全平方数表示,那么就首先来看看能填入的最大完全平方数是谁,之后再看剩下的那个数字还能不能填下其它的完全平方数,依次类推。但是,通过之前的例子,简答地想一想就可以知道,对于这个例子,贪心算法是不成立的。以12这个数字为例,如果我们一上来就填入最大的完全平方数,那应该是9,剩下的是3,对于3这个数字,我们只能用3个1表示了,这样的12是被4个完全平方数表示的。可是,12可以用3个4,3个完全平方数就表示了12这种解法,因此贪心算法是不成立的。贪心算法这种算法虽然实现起来会比较简单,而且想起来也比较简单,但是我们很容易陷入一个误区,就是一个问题不能使用贪心算法来解,但是却错误地任务它可以用贪心算法来解决。

 

如果这个问题不能用贪心算法来解决的话,那么我们有什么解决思路呢?

对整个问题进行这样一个建模,把它转化为一个图论的问题。对于图论的问题,就需要创建一张图,对于图来说,就要定义节点和边。对于这个问题,将从0到n每一个数字都定义成一个节点,如果两个数字x到y相差一个完全平方数的话,则连接成一条边,这样,就得到了一个无权图。所以,整个问题,就转化成了,在这个无权图中,从n到0的最短路径。

 

如果n=4的话,换句话说,从0到4一共有5个节点的话,图为如下所示。从4到3,从3到2,从2到1,从1到0各有一条边相连,这是因为它们相差的是1这样一个完全平方数。于此同时,从4到0也有一条边连接,这是因为它们之间相差了4这么一个完全平方数。下面这个图建立的是一个有向图,永远从大的数指向小的数,这样一来就可以去搜索,比如当前n=4的话,从这张图里找到从4这个节点到0这个节点的路径。

 

如果n=5的话,相应地多这样一个节点,与此同时,5-1=4,5-4=1,5这个节点就需要和另外2个节点相邻。

 

类似地,如果有6这个节点的话,6可以到达5,6也可以到达2这个节点,

 

如果有7这个节点的话,7可以到达6,7也可以到达3,相差1和4,

 

如果有8的话,那么8可以到达4,8也可以到达7,也是相差1和4,

 

如果有9这个节点,那么9可以-1得8,-4得5,也可以-9得0,相差的是1、4、9这三个完全平方数。所以,9这个节点,还和其它的三个节点相连接,依次类推。

 

 

当建好这个图以后,就可以根据最短路径求出这个问题的解。

曾经在树中进行层序优先遍历,其实就是广度优先遍历,其中,首先需要一个队列。

队列中保存的是键值对。键是第几个数字,值表示的是建立的图中经历了几段路径走到了这个数字。

 

class Solution {
    public int numSquares(int n) {
        int[] memo=new int[n+1];
        
        memo[1]=1;
        
        for(int i=2;i<=n;i++ ){
            memo[i]=Integer.MAX_VALUE;
            for(int j=1;j*j<=i;j++){
               if(j+j==i){
                    memo[i]=min3(memo[i],2,1+memo[i-j*j]);
                }else{
                    memo[i]=Math.min(memo[i],1+memo[i-j*j]);
                }
            }
        }
        
        return memo[n];
    }
    
    
      private int min3(int a,int b,int c){
        return Math.min(a,Math.min(b,c));
    }
   
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值