leetcode 368. 最大整除子集 中等
给你一个由 无重复 正整数组成的集合 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]
提示:
1 <= nums.length <= 1000
1 <= nums[i] <= 2 * 10^9
nums
中的所有整数 互不相同
分析:
状态定义:dp[i] 表示在输入数组 nums 升序排列的前提下,以 nums[i] 为最大整数的「整除子集」的大小(在这种定义下 nums[i] 必须被选择)。
状态转移方程:枚举 j=0…i−1 的所有整数 nums[j],如果 nums[j] 能整除 nums[i],说明 nums[i] 可以扩充在以 nums[j] 为最大整数的整除子集里成为一个更大的整除子集。
初始化:由于 nums[i] 必须被选择,因此对于任意 i=0…n−1,初始的时候 dp[i]=1,这里 n 是输入数组的长度。
输出:由于最大整除子集不一定包含 nums 中最大的整数,所以我们需要枚举所有的 dp[i],选出最大整除子集的大小 maxSize,以及该最大子集中的最大整数 maxVal。按照如下方式倒推获得一个目标子集:
倒序遍历数组 dp,直到找到 dp[i]=maxSize 为止,把此时对应的 nums[i] 加入结果集,此时 maxVal=nums[i];
然后将 maxSize 的值减 1,继续倒序遍历找到 dp[i]=maxSize,且 nums[i] 能整除 maxVal 的 i 为止,将此时的 nums[i] 加入结果集,maxVal 更新为此时的 num[i];
重复上述操作,直到 maxSize 的值变成 0,此时的结果集即为一个目标子集。
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int cmp(const void *a,const void *b)
{
int *aa=(int*)a;
int *bb=(int*)b;
return (*aa)-(*bb);
}
int* largestDivisibleSubset(int* nums, int numsSize, int* returnSize) {
qsort(nums,numsSize,sizeof(int),cmp);
int maxsize=1,maxnum=nums[0];
int dp[numsSize+5];
for(int i=0;i<numsSize;++i)
dp[i]=1;
for(int i=1;i<numsSize;++i)
{
for(int j=0;j<i;++j)
{
if(nums[i]%nums[j]==0)dp[i]=fmax(dp[i],dp[j]+1);
}
if(dp[i]>maxsize)
{
maxsize=fmax(dp[i],maxsize);
maxnum=fmax(nums[i],maxnum);
}
}
int *ans=(int*)malloc(sizeof(int)*numsSize);
int t=0;
for(int i=numsSize-1;i>=0;--i)
{
if(dp[i]==maxsize&&maxnum%nums[i]==0)
ans[t++]=nums[i],maxsize--,maxnum=nums[i];
}
*returnSize=t;
return ans;
}