LeetCode 1819. 序列中不同最大公約數的數目(數論)

題目描述

給你一個由正整數組成的數組 nums

數字序列的 最大公約數 定義爲序列中所有整數的共有約數中的最大整數。

例如,序列 [4,6,16] 的最大公約數是 2
數組的一個 子序列 本質是一個序列,可以通過刪除數組中的某些元素(或者不刪除)得到。

例如,[2,5,10][1,2,1,2,4,1,5,10] 的一個子序列。
計算並返回 nums 的所有 非空 子序列中 不同 最大公約數的 數目

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/number-of-different-subsequences-gcds
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

題目分析

看到 GCD(最大公約數)的題目我就頭大,因爲我除了質因數分解就啥都不知道了。

這題看起來不算難,但是我思考了很久還是沒能獨立做出來,最後還是翻閱了力扣討論區。

我開始想的是 DP 或者排序,又試了下暴力破解(毫無疑問超時)。後面看到數據範圍也不是很大,在思考枚舉每個數是否能夠成功答案,但是沒有能夠繼續思考下去。。。如何判斷該數字是否能夠成爲某些數字的 GCD 呢?

(雖然題目要求是子序列,但其實順序並不重要,下面我就直接說子集)

假設要計算數字 x 是否能成爲某個子集的 GCD ,首先要找出在數組中,能夠成爲 x 的倍數的數字集合(顯然,不能整除 x 的數字,約數不可能有 x)。然後求出這個集合的 GCD 假設爲 gcd(x倍數集合),如果 gcd(x倍數集合) 等於 x 證明這些數字的就是一個滿足 GCD 爲 x 的子集。

不會證明,但結論還是比較直觀的 =。=

由於 \(\sum_{i=1}^n \frac{n}{i} = n*\sum_{i=1}^n \frac{1}{i} = O(n*logn)\)

求 gcd 均攤是 \(O(m*logm)\) 的, 故該算法的複雜度也爲 \(O(m*logm) + O(m*logm)=O(m*logm)\)

https://leetcode-cn.com/problems/number-of-different-subsequences-gcds/solution/kao-lu-mei-yi-ge-shu-shi-fou-ke-yi-cheng-2sb2/

AC 代碼

class Solution {
public:
    int countDifferentSubsequenceGCDs(vector<int>& nums) {
        int maxv = *max_element(nums.begin(), nums.end());
        bool exist[maxv + 1];
        memset(exist, false, sizeof exist);
        for (int x: nums) {
            exist[x] = true;
        }
        int count = 0;
        for (int i = 1; i <= maxv; i++) {
            // 枚舉每一個數字是否可能成爲最小公約數
            int gcdv = 0;
            for (int j = i; j <= maxv; j += i) {
                // 只有當i的倍數(存在在數組中的)的最小公約數爲i纔可以
                if (exist[j]) {
                    gcdv = gcdv ? gcd(gcdv, j) : j;
                }
            }
            if (gcdv == i) {
                count++;
            }
        }
        return count;
    }
};

總結

說實話沒做出來的一部分原因是我有點畏懼 gcd (包括各種數論)的題目,但是解法就還蠻暴力的。另一個原因是我想到了這種方法但是自我否決了,主要是時間複雜度計算出錯。確實這種複雜的時間複雜度計算我完全不會,還是需要加強這方面的能力。

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