动态规划解题步骤:
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;
}
};