动态规划33:1027. 最长等差数列

动态规划解题步骤:

1.确定状态表示:dp[i]是什么

2.确定状态转移方程:dp[i]等于什么

3.初始化:确保状态转移方程不越界

4.确定填表顺序:根据状态转移方程即可确定填表顺序

5.确定返回值

题目链接:1027. 最长等差数列 - 力扣(LeetCode)

题解1(三层for循环超时):

由于不知道等差数列的等差值,所以必须使用二维dp表,以此确定等差值

1.状态表示: dp[i][j]表示以nums[i]和nums[j]结尾的最长等差子序列的长度

2.状态转移方程:如果数组中存在值等于nums[i]-dif,dp[i][j]=max(dp[i][j],dp[k][i]+1)

                            如果不存在,dp[i][j]=2

3.初始化:等差数列的最低值为2,创建dp表时就将它们全部初始化为2

4.填表顺序:从左向右,从上向下,依次填表(只填写二维表上三角部分)

5.返回值:返回dp表上三角部分最大值

class Solution {
public:
    int longestArithSeqLength(vector<int>& nums) {
        //分析:
        //假设存在等差子序列nums[k] nums[i] nums[j]
        //dp[i][j]表示以nums[i]和nums[j]结尾的最长等差子序列的长度
        //dif=nums[j]-nums[i]
        //状态转移方程:
        //如果nums[k]=nums[i]-dif存在
        //dp[i][j]=max(dp[i][j],dp[k][i]+1)
        //如果不存在
        //dp[i][j]=2
        //上述0<=k<i

        size_t n=nums.size();
        //创建dp表
        vector<vector<int>> dp(n,vector<int>(n,2));
        //初始化
        //dp表被初始化为2,因为等差序列的最低长度为2
        //填表
        for(int j=0;j<n;++j)
        {
            for(int i=0;i<j;++i)
            {
                int dif=nums[j]-nums[i];
                for(int k=0;k<i;++k)
                {
                    if(nums[k]==nums[i]-dif) 
                        dp[i][j]=max(dp[i][j],dp[k][i]+1);
                }
            }
        }
        //返回值
        //返回dp表(上三角部分)中的最大值
        int ans=0;
        for(auto row:dp)
            for(auto value:row)
                if(value>ans) 
                    ans=value;
        return ans;
    }
};

题解2(两层for循环,使用hash代替一层for循环):

查找nums[k]时,不再使用for循环查找,而是使用hash表查找:将nums值和其对应的下标绑定到hash表中,由于本题中存在重复元素,所以不能在填写dp表之前就填写完成hash表,这样会导致重复元素的下标被覆盖,因此要一遍填写dp表一遍填写hash表

边dp边hash:对于已确定的nums[i] nums[j],我们需要查找其前面的值是否存在nums[k],所以我们只需要nums[i]前面的值,如果将nums[i]后面的值夜填入hash表,如果存在和nums[i]前面值重复元素的话,下标值会被覆盖,因此边dp边hash时只能将nums[i]前面的值填入hash表

只将nums[i]前面的值填入hash表:由于常规思想是先固定等差数列最后一个值nums[j],在固定倒数第二个值nums[i],这样的话再遍历时nums[i]是不断变化的(参见题解1代码),因此要先固定nums[i],再固定nums[j]

此外在将nums[i]前面的值填入hash表时,不能将nums[i]也填入,所以要在循环体循环一次后再开始填入hash表

class Solution {
public:
    int longestArithSeqLength(vector<int>& nums) {
        //三层for循环超时,使用hash表查找nums[k]减少一层for循环
        //创建hash表:nums值与nums值的下标绑定

        size_t n=nums.size();
        //创建hash表,并将nums值与其下标绑定
        unordered_map<int,int> hash;
        // for(int i=0;i<n;++i)
        //     hash[nums[i]]=i;
        //创建dp表
        vector<vector<int>> dp(n,vector<int>(n,2));
        //初始化
        //等差数列最低值为2,dp表初始化为2
        //填表
        for(int i=0;i<n;++i)//固定倒数第二个数
        {
            for(int j=i+1;j<n;++j)//固定倒数第一个数
            {
                int dif=nums[j]-nums[i];
                //使用hash表查找代替for循环查找
                int temp=nums[i]-dif;
                if(hash.count(temp))
                {
                    int k=hash[temp];
                    if(k<i)
                        dp[i][j]=dp[k][i]+1;
                }
            }
            hash[nums[i]]=i;
        }
        //返回值
        //返回dp表(上三角部分)中的最大值
        int ans=0;
        for(auto row:dp)
            for(auto value:row)
                if(value>ans) 
                    ans=value;
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南林yan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值