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));
}
}