1. 程式人生 > >279. Perfect Squares (完全平方數)

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.

Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.

又是超哥一個人辛苦的更新題目,一個人托起LeetCode免費題的一片天空啊,贊一個~ 這道題說是給我們一個正整數,求它最少能由幾個完全平方陣列成。這道題是考察四平方和定理,to be honest, 這是我第一次聽說這個定理,天啦擼,我的數學是語文老師教的麼?! 閒話不多扯,回來做題。先來看第一種很高效的方法,根據四平方和定理,任意一個正整數均可表示為4個整數的平方和,其實是可以表示為4個以內的平方數之和,那麼就是說返回結果只有1,2,3或4其中的一個,首先我們將數字化簡一下,由於一個數如果含有因子4,那麼我們可以把4都去掉,並不影響結果,比如2和8,3和12等等,返回的結果都相同,讀者可自行舉更多的栗子。還有一個可以化簡的地方就是,如果一個數除以8餘7的話,那麼肯定是由4個完全平方陣列成,這裡就不證明了,因為我也不會證明,讀者可自行舉例驗證。那麼做完兩步後,一個很大的數有可能就會變得很小了,大大減少了運算時間,下面我們就來嘗試的將其拆為兩個平方數之和,如果拆成功了那麼就會返回1或2,因為其中一個平方數可能為0. (注:由於輸入的n是正整數,所以不存在兩個平方數均為0的情況)。注意下面的!!a + !!b這個表示式,可能很多人不太理解這個的意思,其實很簡單,感嘆號!表示邏輯取反,那麼一個正整數邏輯取反為0,再取反為1,所以用兩個感嘆號!!的作用就是看a和b是否為正整數,都為正整數的話返回2,只有一個是正整數的話返回1,參見程式碼如下:

class Solution {
public:
    int numSquares(int n) {
        while (n % 4 == 0) n /= 4;
        if (n % 8 == 7) return 4;
        for (int a = 0; a * a <= n; ++a) {
            int b = sqrt(n - a * a);
            if (a * a + b * b == n) {
                return !!a + !!b;
            }
        }
        return
3; } };

筆記1:從內在數學關係中找到解題方法,進而轉換成程式;相比於程式設計,更重要的是數學推理。

這道題遠不止這一種解法,我們還可以用動態規劃Dynamic Programming來做,我們建立一個長度為n+1的一維dp陣列,將第一個值初始化為0,其餘值都初始化為INT_MAX, i從0迴圈到n,j從1迴圈到i+j*j <= n的位置,然後每次更新dp[i+j*j]的值,動態更新dp陣列,其中dp[i]表示正整數i能少能由多個完全平方陣列成,那麼我們求n,就是返回dp[n]即可,也就是dp陣列的最後一個數字,參見程式碼如下:

// DP
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX);   //dp[]陣列初始化
        dp[0] = 0;
        for (int i = 0; i <= n; ++i) {
            for (int j = 1; i + j * j <= n; ++j) {
                dp[i + j * j] = min(dp[i + j * j], dp[i] + 1);  //狀態轉移方程
            }
        }
        return dp[n];
    }
};

筆記2:採用動態規劃,逐步儲存各階段答案的中間值,最後輸出dp[n]即為最終結果,但在效率上不如解法一,dp的時間複雜度為O(n^2)。