zoukankan      html  css  js  c++  java
  • [LeetCode] 368. Largest Divisible Subset

    Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of elements in this subset satisfies:

    Si % Sj = 0 or Sj % Si = 0.

    If there are multiple solutions, return any subset is fine.

    Example 1:

    Input: [1,2,3]
    Output: [1,2] (of course, [1,3] will also be ok)
    

    Example 2:

    Input: [1,2,4,8]
    Output: [1,2,4,8]

    最大整除子集。题意是给一个整数数组,请你找出最长的一个子串,子串满足任意两个数之间可以互相整除,即任意两个数之间互相整除的时候余数为0。比如两个数A和B好了,A%B = 0或者B%A = 0都可以满足题意。

    思路是DP动态规划。这个题的思路跟300题很像,但是这个题是可以对input的排序的。首先,两个不同的数字A和B,既然不同,那么一定一大一小,那么一定意味着小的除以大的的余数一定不为0。题目既然规定了A%B = 0或者B%A = 0都可以满足题意,我们可以先将input从小到大排序。这样扫描数组的时候可以从当前位置往前看,看当前数字nums[i]是否可以整除比他小的数字。

    接着创建一个数组 count[i],记录的是以 nums[i] 结尾的,最大的满足题意的子集大小。这个数组 count[i] 初始化的时候每个位置都是1,因为每个数字除以他本身的余数都为0。接着会用到两个for loop,一个是遍历nums数组,一个是将 nums[i] 去跟比他自己小的所有的数字都去做一下除法,看看有多少数字能跟 nums[i] 相除 == 0。此时 count[i] 数组会被改写。

    接着再次扫描 count[i] 数组,找出最大值maxIndex,这个变量表示的是集合的最大长度的下标在什么地方。这个最大值是最长的子串的长度。同时找到的最大值背后的 nums[maxIndex] ,也是这个子串的最后一个元素。

    最后一步是从 nums[maxIndex] 开始往前按照一个虚拟的path扫描,如果遇到任何一个数字能被 nums[maxIndex] 整除,则把这个数字加入结果集。什么叫做path呢?比如我们给一个例子[4, 8, 10, 240]。我们能找到最大值是240,而且maxIndex会从240的下标3开始往前走,如果什么都不做,就会直接判断下一个数字10。10就比较tricky了,他可以被240整除但是他其实是不在最后的结果集里面的。我们必须额外去记录每当一个数字符合条件被加入结果集之后,curCount要--,这样只有跟 count[i] 能对应的上的curCount背后的数字才是这一个子集里面的数字。

    时间O(n^2)

    sort - O(nlogn)

    扫描,写入count[i]的值 - O(n^2)

    空间O(n)

    Java实现

     1 class Solution {
     2     public List<Integer> largestDivisibleSubset(int[] nums) {
     3         List<Integer> res = new ArrayList<>();
     4         int n = nums.length;
     5         // corner case
     6         if (n == 0) {
     7             return res;
     8         }
     9 
    10         // normal case
    11         Arrays.sort(nums);
    12         // 以nums[i]结尾的,最大的满足题意的子集大小
    13         int[] count = new int[nums.length];
    14         Arrays.fill(count, 1);
    15         for (int i = 1; i < nums.length; i++) {
    16             for (int j = i - 1; j >= 0; j--) {
    17                 if (nums[i] % nums[j] == 0) {
    18                     count[i] = Math.max(count[i], count[j] + 1);
    19                 }
    20             }
    21         }
    22 
    23         // 集合的最大长度在什么地方
    24         int maxIndex = 0;
    25         for (int i = 1; i < nums.length; i++) {
    26             maxIndex = count[i] > count[maxIndex] ? i : maxIndex;
    27         }
    28 
    29         // temp是子集里面最大的数字
    30         int temp = nums[maxIndex];
    31         // curCount是子集的size
    32         int curCount = count[maxIndex];
    33         for (int i = maxIndex; i >= 0; i--) {
    34             if (temp % nums[i] == 0 && count[i] == curCount) {
    35                 res.add(nums[i]);
    36                 temp = nums[i];
    37                 curCount--;
    38             }
    39         }
    40         return res;
    41     }
    42 }

    LIS类相关题目

    LeetCode 题目总结

  • 相关阅读:
    ASP.NET Core 微信支付(一)【统一下单 APIV3】
    ASP.NET Core 跨域
    快速排序
    希尔排序(插入式与位移式优化)
    选择排序和插入排序
    冒泡排序
    八皇后问题
    递归与迷宫回溯问题
    逆波兰计算器
    栈实现综合计算器
  • 原文地址:https://www.cnblogs.com/cnoodle/p/13124054.html
Copyright © 2011-2022 走看看