LeetCode279:完全平方數,動態規劃解法超過46%,作弊解法卻超過97%

歡迎訪問我的GitHub

這裏分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

本篇概覽

本篇概覽

  • 這是道高頻面試題,值得一看
  • 首先,這道題的難度是中等
  • 來看題目描述:
給你一個整數 n ,返回 和爲 n 的完全平方數的最少數量 。

完全平方數 是一個整數,其值等於另一個整數的平方;換句話說,其值等於一個整數自乘的積。例如,1、4、9 和 16 都是完全平方數,而 3 和 11 不是。
  • 示例1:
輸入:n = 12
輸出:3 
解釋:12 = 4 + 4 + 4
  • 示例 2:
輸入:n = 13
輸出:2
解釋:13 = 4 + 9
  • 提示:
1 <= n <= 104

解題思路

  • 該題的解題思路是動態規劃,核心解法有兩點:
  1. 數字i,可能是某個數字的平方,例如數字9是數字3的平方
  2. 數字i,如果不是某個數字的平方,該數字能用此表達式表達:i = i - j*j + j*j
  • 對於上述第二種情況,就是動態規劃狀態轉移方程的核心啦!
  • 假設dp[i]的定義是數字i的完全平方數的最少數量,那麼表達式i = i - j*j + j*j就很容易用來分析dp[i]了
    在這裏插入圖片描述
  • 簡單地說,就是:dp[i] = dp[i-j*j] + 1
  • 當然了,上述只是最基本的推測,不代表已經解完了,還剩一個重點:j到底是幾?
  • 以10爲例,10=(10-3*3) + 3*3,但是這不是唯一,還有10=(10-2*2) + 2*2,所以到底j等於幾?根據題意,應該是dp[10-3*3]和dp[10-2*2]中最小的那個
  • 至此,分析完畢,可以愉快的寫代碼了

編碼

  • 完整源碼如下所示,可見,對應前面分析的j的多種可能,要取最小值
class Solution {
    public int numSquares(int n) {
        // i = i-j*j + j*j  - 注意這個j*j,就是完全平方數中的一個

        // dp[i]定義:數字i的完全平方數
        int[] dp = new int[n+1];

        dp[0] = 1;

        for (int i=1;i<=n;i++) {
            dp[i] = Integer.MAX_VALUE;

            for(int j=1;j*j<=i;j++) {
                // 如果出現i等於某個數字的平方,那麼i的完全平方數就是1
                if (j*j==i) {
                    dp[i] = 1;
                    break;
                }

                // +1的意思就是j*j表示完全平方數中的一個
                dp[i] = Math.min(dp[i-j*j]+1, dp[i]);
            }
        }

        return dp[n];
    }
}
  • 編碼完成後提交,順利AC,只是成績很不理想,僅超過45%,如下圖
    在這裏插入圖片描述

反思,爲啥成績這麼差?

  • 這麼簡單的動態規劃操作,爲何成績這麼落後?
  • 於是,我想到了一種可能:說不定可以作弊...
  • 理由有二
  1. 首先,這道題的輸入是個數字,輸出也是個數字,那就存在提前算好的可能,然後按輸入返回提前算好的記過
  2. 其次,也是最關鍵的,就是題目要求中的那句提示,如下圖,n小於等於一萬,所以,我只要存一萬個數字的對應關係,就行了唄:
    在這裏插入圖片描述
  • 看到這裏,聰明的您應該知道我要如何作弊了,沒錯,就是把每個數字的完全平方數算出來,改動如下圖
    在這裏插入圖片描述
  • 然後,運行上述代碼,入參是10000,即可在控制檯得到一個字符串,那就是從0到10000,每個數字的完全平方數
  • 接下來的要做的就很簡單了,如下所示,用上述字符串做成一個int數組array,然後numSquares方法中就一行代碼,返回入參n對應的完全平方數就行了
class Solution {
	// 數組的值就是剛纔打印出來的字符串,太長了,就不完全貼出來了
    private int[] array = new int [] {1,1,2,3,1,2,3,4,2,1...};
    public int numSquares(int n) {
        return array[n];
    }
}
  • 至此,就一行代碼了,相信成績不會差了吧,運行一下試試,如下圖,大跌眼鏡了,一行代碼也要45ms,從之前的超過45%跌落到超過22%

在這裏插入圖片描述

  • 突如其來的丟臉...
    在這裏插入圖片描述
  • 好吧,讓我對着這一行代碼捋捋,代碼太少了,很容易捋清楚,如下圖
    在這裏插入圖片描述
  • 找到了問題,改起來也就很容易了,如下圖黃框所示,這一下,array數組在編譯成class文件的時候被丟進了常量區,每次創建Solution實例的時候,不會再去創建array對象了
    在這裏插入圖片描述
  • 再次提交,這一回,作弊成功,用時和內存消耗雙雙超過百分之九十七
    在這裏插入圖片描述
  • 總的來說,動態規劃是正解,如果條件允許,也能用歪門邪道作弊試試,可以開闊思路,同時取得好成績,令人身心愉悅

歡迎關注博客園:程序員欣宸

學習路上,你不孤單,欣宸原創一路相伴...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章