【Lintcode】513. Perfect Squares

题目地址:

https://www.lintcode.com/problem/perfect-squares/description

给定一个正整数 n n n,问其能最少写成多少个完全平方数之和。完全平方数从 1 1 1开始。

法1:数论。设答案是 x x x。首先由拉格朗日四平方和定理(Lagrange’s Four-Square Theorem)知道, x ≤ 4 x\le 4 x4。注意到如果 4 ∣ n 4|n 4n,那么有 f [ n ] = f [ n / 4 ] f[n]=f[n/4] f[n]=f[n/4],证明如下:首先可以把 n / 4 n/4 n/4的分解中的每个平方数乘以 2 2 2得到 n n n的一个分解,所以有 f [ n ] ≤ f [ n / 4 ] f[n]\le f[n/4] f[n]f[n/4]。接着,如果存在某个 n n n的分解 n = a 2 + b 2 + c 2 + d 2 n=a^2+b^2+c^2+d^2 n=a2+b2+c2+d2使得 a , b , c , d a,b,c,d a,b,c,d都是偶数,那么显然 f [ n / 4 ] ≤ f [ n ] f[n/4]\le f[n] f[n/4]f[n],结论成立。如果其中有一个是奇数,那么由 4 ∣ n 4|n 4n知道 f [ n ] = 4 f[n]=4 f[n]=4,再由四平方和定理知道 4 = f [ n ] ≤ f [ n / 4 ] ≤ 4 4=f[n]\le f[n/4]\le 4 4=f[n]f[n/4]4,知道 f [ n / 4 ] = 4 f[n/4]=4 f[n/4]=4,所以结论也成立。
由上知,可以直接考虑 n n n不是 4 4 4的倍数的情况。由勒让德三平方和定理(Legendre’s Three-Square Theorem), x = 4 x=4 x=4当且仅当 n ≡ 7 ( m o d    8 ) n\equiv 7(\mod 8) n7(mod8)(原定理是这么说的,如果某个自然数 n n n能表为三个整数的平方和,当且仅当 n n n不能写成 4 a ( 8 b + 7 ) 4^a(8b+7) 4a(8b+7)的形式。那么也就是说,如果 4 ∤ n 4\nmid n 4n,由于 n n n能写成少于 3 3 3个整数平方和,必然能写成恰好 3 3 3个整数平方和,补 0 2 0^2 02就行,所以 n ≡ 7 ( m o d    8 ) ⇔ x = 4 n\equiv 7(\mod 8)\Leftrightarrow x=4 n7(mod8)x=4)。如果 n n n是完全平方数那么显然 x = 1 x=1 x=1,如果 n n n不是完全平方数但能表为两个平方数之和,显然 x = 2 x=2 x=2。判断完 x x x是否是 1 , 2 , 4 1,2,4 1,2,4之后如果仍然不成立,那么就知道 x = 3 x=3 x=3了。代码如下:

public class Solution {
    public int numSquares(int n) {
    	// 把4除干净
        while (n % 4 == 0) {
            n /= 4;
        }
        
        // 套用勒让德定理
        if (n % 8 == 7) {
            return 4;
        }
        
        // n是完全平方数,则返回1
        if (isSquare(n)) {
            return 1;
        }
        
        // 判断n是否可以表为两个平方数之和
        for (int i = 1; i * i <= n; ++i) {
            if (isSquare(n - i * i)) {
                return 2;
            }
        }
        
        // 剩余的情况就是3了
        return 3;
    }

	// 开个函数用于判断n是否是完全平方数
    boolean isSquare(int n) {
        int sq = (int) Math.sqrt(n);
        return n == sq * sq;
    }
}

时间复杂度 O ( n / k + k ) O(n/k+k) O(n/k+k),其中 n = 4 s k n=4^sk n=4sk 4 ∤ k 4\nmid k 4k

法2:动态规划。设 f [ i ] f[i] f[i] i i i能表示的最少完全平方数之和的个数。那么有 f [ i ] = min ⁡ 1 ≤ j 2 ≤ i ( f [ i − j 2 ] + 1 ) f[i]=\min_{1\le j^2\le i} (f[i-j^2]+1) f[i]=min1j2i(f[ij2]+1)。代码如下:

import java.util.Arrays;

public class Solution {
    /**
     * @param n: a positive integer
     * @return: An integer
     */
    public int numSquares(int n) {
        // write your code here
        // 稍微做一下优化
        while (n % 4 == 0) {
            n /= 4;
        }
        
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
    
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j * j <= i; j++) {
                dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
            }
        }
        
        return dp[n];
    }
}

时间复杂度 O ( n 3 2 ) O(n^{\frac{3}{2}}) O(n23)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值