leetcode-368. 最大整除子集

給出一個由無重複的正整數組成的集合,找出其中最大的整除子集,子集中任意一對 (Si,Sj) 都要滿足:Si % Sj = 0 或 Sj % Si = 0。

如果有多個目標子集,返回其中任何一個均可。

示例 1:

輸入: [1,2,3]
輸出: [1,2] (當然, [1,3] 也正確)

示例 2:

輸入: [1,2,4,8]
輸出: [1,2,4,8]

一、思路

一開始,我的思路是先找到一個集合,裏面的元素滿足整除子集的要求,然後用從數組中取出元素與集合裏面的每一個元素作取餘運算(%),看看是否滿足條件。

很明顯,這個算法一定會超時,超時原因在於每次取出一個新的數要對整個集合執行一遍取餘運算,很花時間,後來我觀察到了整除子集的一個重要性質:

設集合A是一個按序排列的整除集合,裏面包含n個實數:a1,a2,…an

若實數滿足:s % an ==0,那麼對任意的i(i <= n)都有:s % ai ==0

也就是說如果一個數能被整除集合中的最大數整除,那麼這個數一定能被整除集合中的所有數整除,證明也很簡單,因爲整除集合中的最大數能夠整除該集合中的所有元素。

知道這個性質後,可以使用動態規劃來解決。

(一)動態規劃

首先對數組排序(每次只需要找到最大值)
設置一個數組dp,dp[i]表示:以nums[i]爲最大值的整除集合,其最多可包含dp[i]個元素

我們找到dp數組最大值,就可以知道最大整除子集的大小和它的最大元素的位置。

C++代碼:

class Solution {
public:
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        vector<int> ans;
        if(nums.size() == 0 || nums.size() == 1)
            return nums;
        // 這裏必須注意到:若子集中任意兩個數都有這個性質,那麼對子集元素a排序之後,若S % an == 0,則S可以加入該子集
        // 因爲an % ai == 0, i = 0, 1, 2, ....
        // dp[i]表示,以nums[i]爲最大值的符合要求的子集所包含的最大數量
        sort(nums.begin(), nums.end());
        vector<int> dp(nums.size(), 1);
        int max_pos = 0, max_len = 0;
        for(int i = 1; i < nums.size(); i++){
            int max_i = 0;
            for(int j = i - 1; j >= 0; j--){
                if(nums[i] % nums[j] == 0){
                    if(dp[j] > max_i)
                        max_i = dp[j];
                }
            }
            dp[i] += max_i;
            if(dp[i] > max_len){
                max_len = dp[i];
                max_pos = i;
            }
        }
        // 找出子集
        ans.push_back(nums[max_pos]);
        int j = max_pos;
        for(int i = max_pos - 1; i >= 0; i--){
            if(nums[j] % nums[i] == 0 && dp[i] + 1 == dp[j]){
                ans.push_back(nums[i]);
                j = i;
            }
        }
        return ans;
    }
};

在這裏插入圖片描述

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