力扣 368. 最大整除子集 dp DAG上最长路

129 篇文章 2 订阅
26 篇文章 0 订阅
本文探讨了LeetCode问题`largest-divisible-subset`的解决方案,通过比较spfa求最长路径与将图转换为有向无环图并用dp求解的两种方法。讲解了如何利用排序简化图结构,以及如何分别使用这两种算法求解最大可整除子集。
摘要由CSDN通过智能技术生成

https://leetcode-cn.com/problems/largest-divisible-subset/
在这里插入图片描述
思路:若 n u m s [ i ] nums[i] nums[i]可以整除 n u m s [ j ] nums[j] nums[j],我们认为从 i i i j j j有一条边,那么 O ( n 2 ) O(n^2) O(n2)处理数组后就得到了一张图,问题就转换为求这张图上的最长路径。有两种做法,第一spfa求最长路;第二先把图转换为有向无环图,然后利用dp求DAG上最长路。先给出第一种做法的代码:

class Solution {
public:
    vector<vector<bool>> m;
    vector<int> dis;
    vector<int> pre;
    int n;

    void spfa(int st)
    {
        vector<bool> inque(n);
        queue<int> q;
        dis[st]=1;
        inque[st]=1;
        q.push(st);
        while(!q.empty())
        {
            int fon=q.front();
            inque[fon]=0;
            q.pop();
            for(int i=0;i<n;i++)
            {
                if(m[fon][i]&&dis[i]<dis[fon]+1)
                {
                    dis[i]=dis[fon]+1;
                    pre[i]=fon;
                    if(!inque[i])
                        inque[i]=1,q.push(i);
                }
            }
        }
    }

    vector<int> largestDivisibleSubset(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        n=nums.size();
        m.resize(n,vector<bool>(n));
        dis.resize(n);
        pre.resize(n,-1);
        for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++)
                if(nums[j]%nums[i]==0)
                    m[i][j]=1;
        for(int i=0;i<n;i++)
            if(!dis[i])
                spfa(i);
        int last=0;
        for(int i=1;i<n;i++)
            if(dis[i]>dis[last])
                last=i;
        vector<int> ans;
        while(last>=0)
        {
            ans.push_back(nums[last]);
            last=pre[last];
        }
        return ans;
    }
};

下面我们来看第二种做法,如果要把一张图转换为有向无环图,则需要做拓扑排序,但是没有必要这么麻烦。只要我们先把数组排序,保证每一条有向边 ( i , j ) (i,j) (i,j)都满足 i < j i<j i<j,那么我们得到的一定是一张有向无环图,直接 d p dp dp即可。

class Solution {
public:
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int n=nums.size();
        int maxsiz=0,curnum=0;
        vector<int> dp(n);
        for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++)
                if(nums[j]%nums[i]==0)
                {
                    dp[j]=max(dp[j],dp[i]+1);
                    if(dp[j]>maxsiz)
                        maxsiz=dp[j],curnum=nums[j];
                }
        vector<int> ans;
        for(int i=n-1;i>=0;i--)
        {
            if(dp[i]==maxsiz&&curnum%nums[i]==0)
            {
                ans.push_back(nums[i]);
                --maxsiz;
                curnum=nums[i];
            }
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值