1.题目:
给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足:
answer[i] % answer[j] == 0 ,或
answer[j] % answer[i] == 0
如果存在多个有效解子集,返回其中任何一个均可。
示例 1:
输入:nums = [1,2,3]
输出:[1,2]
解释:[1,3] 也会被视为正确答案。
示例 2:
输入:nums = [1,2,4,8]
输出:[1,2,4,8]
来源:力扣(LeetCode)
链接:368.最大整除子集
思路:
虽然示例中给出的测试值都是有序的,其实应该先进行排序。
原始的思路是两次遍历。
该问题是一个序列DP问题:状态的转移依赖前一个状态。
定义:dp[i]为考虑前i个数字,且以第i个数位结尾的最长[整除子集]长度。
存在两种情况:
- 如果在i之前找不到符合的nums[i]%nums[j]==0的位置j,那么nums[i]不能接在位置i的任何数后面,此时dp[i]=1
- 如果在i之前能够找到符合条件的位置j,则取所有符合条件的f[j]的最大值,代表如果希望找到以nums[i]为结尾的最长[整除子集],需要将nums[i]接到符合条件的最长的nums[j]后面,此时dp[i] = dp[j]+1
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
public class LargestDivisibleSubset {
public List<Integer> largestDivisibleSubset(int[] nums)
{
Arrays.sort(nums);
int n = nums.length;
int[] dp = new int[n];
int[] backup = new int[n];
for (int i = 0;i < n;i++)
{
System.out.println(nums[i]+":");
//至少包含自身一个数,因此起始长度为1,由自身转移得到
int len = 1, prev = i;
for (int j = 0;j < i;j++)
{
if (nums[i] % nums[j] == 0)
{
System.out.format("nums[%d]:%d/nums[%d]:%d\n",i,nums[i],j,nums[j]);
//如果能接在更长的序列后面,则更新最长序列和由何转移得到
if (f[j] + 1 > len)
{
System.out.format("len:%d=f[%d]:%d+1\n",len,j,dp[j]);
len = dp[j] + 1;
prev = j;
}
}
}
//记录最终长度和从何转移而来
dp[i] = len;
backup[i] = prev;
System.out.println("");
}
//遍历所有的f[i],取得最长
int max = -1, idx = -1;
for (int i = 0;i < n;i++)
{
if (dp[i] > max)
{
idx = i;
max = dp[i];
}
}
//使用g[]数组回溯出具体方案
List<Integer> ans = new ArrayList<>();
while (ans.size() != max)
{
ans.add(nums[idx]);
idx = backup[idx];
}
return ans;
}
public static void main(String[] args) {
LargestDivisibleSubset LDS = new LargestDivisibleSubset();
List<Integer> res;
int []nums = {1,2,4,8};
res = LDS.largestDivisibleSubset(nums);
for (int i = 0;i < res.size();i++)
{
System.out.format("%d\t", res.get(i));
}
}
}