leetcode368 最大整除子集

leetcode 368 Largest Divisible Subset

Posted by BY on June 27, 2020

前言

持续更新了

正文

问题来源

本问题来自leetcode上的368题。

问题描述

给出一个由无重复的正整数组成的集合,找出其中最大的整除子集,子集中任意一对 (Si,Sj) 都要满足:Si % Sj = 0 或 Sj % Si = 0。
如果有多个目标子集,返回其中任何一个均可。

示例 1:

输入: [1,2,3]
输出: [1,2] (当然, [1,3] 也正确)

示例 2:

输入: x = 2, y = 6, z = 5
输出: False

分析:

类似并查集的思想,先讲讲并查集问题。啥假设a,b,c;a,b是朋友;a,c也是朋友;然后a,b,c可以分成一类。做法是初始化时,a,b,c都有一个lead指针指向自身,当有条关系a,b则将follower少的那个指向另外一个,即此时a的leader指针指向自身,b的leader指针指向a;当a,c关系时,会得到a的leader指针指向自身,c的leader指针指向a;当所有节点的递归lead都是同一个时,则可以认为他们是一个团体。
回到这道题,首先将原数组从大到小排序,有两个数字a,b,a在前b在后(b>a)若b mod a == 0,此时有个c数字,c大于a,b,若c mod b == 0,则 c mod a也一定等于0,所以可以直接记录最大的前置即可。
初始化情况,每个元素的前置都是自身,有自身元素的集合元素个数为1。
使用一个一维DP,其含义是题目要求的数组,DP[i]的含义是,从0~i位置满足题目的最长数组。初始化每个dp元素都为1。
使用一个一维parent,记录每个元素的前置,初始化情况,每个元素的前置都是自身。
先用i遍历每个数字,然后用j从后向前寻找能被nums[i]整除的数字,这样如果判断能整除的时候,再判断dp[i] < dp[j] + 1,即对于以i索引结尾的最长的数组是否变长了。在变长的情况下,需要更新dp[i],同时使用parent[i]更新i的前面能整除的数字。另外还要统计对于整个数组最长的子数组长度。
知道了对于每个位置最长的子数组之后,我们也就知道了对于0~n区间内最长的满足题目条件的数组,最后需要再次遍历,使用parent就能把正儿个数组统计输出出来。因为这个最大的索引mx_index是对n而言的,所以输出是逆序的。

func largestDivisibleSubset(nums []int) []int {
    if len(nums) == 0 {
        return nil
    }
    sort.Ints(nums)
    //fmt.Println(nums)
    parent := make([]int, len(nums))
    dp := make([]int, len(nums))
    for i := 0; i < len(nums); i++ {
        parent[i] = i
        dp[i] = 1
    }
    cmax := 1
    k := 0
    for i := 1; i < len(nums); i++ {
        for j := 0; j < i; j++ {
            if nums[i] % nums[j] != 0 {
                continue
            }
            if dp[j] + 1 > dp[i] {
                dp[i] = dp[j] + 1
                parent[i] = j
            }
            if dp[i] > cmax {
                cmax = dp[i]
                k = i
            }
        }
    }
    res := make([]int, 0, cmax)
    for parent[k] != k {
        res = append(res, nums[k])
        k = parent[k]
    }
    res = append(res, nums[k])
    return res
}

总结:

勤思考。

结语

不管怎么样好好加油。