- 1.线性动态规划
- 2.例题练习
- 最长递增子序列
- 最长递增子序列的个数
线性动态规划的特点是:状态推导按照i从小到大进行推导。
大规模的解答依靠小规模的解答。
解决动态规划问题大体上分为:
- 问题的Base Case情况(最简单的情况)
- 问题的状态有哪些
- 什么选择问题的状态可能会改变
- 如何定义dp数组表达状态和选择(状态转移)
线性动态规划状态定义一般为:
dp[n]:0~n符合题干的答案
状态转移一般有两种情况:
1. 依赖比 i 小的 O(1) 个子问题
dp[n] = f(dp[n-1], ..., dp[0]); 其中f一般为最大值和最小值函数。具体情况具体分析
2. 依赖比 i 小的 O(n) 个子问题
此时dp[n] 与此前的更小规模的所有子问题 dp[n - 1], dp[n - 2], …, dp[1] 都可能有关系
for(int i=1;ifor(int j=1;j dp[i]=f(dp[i],f(dp[j])) } } 其中f一般为最大值和最小值函数。具体情况具体分析
线性动态规划分为:单串,双串,矩阵问题等,这里先练习单串最经典的LIS问题。
2.例题练习 最长递增子序列LeetCode原题链接
思路:输入是一个单串,首先思考单串问题中设计状态
- dp[i]数组定义:代表数组下标为i时最长子序列。包括数组下标为i的元素。
- 初始状态:当i=0时,数组只有一个元素,所以dp[0]=1。
- 每次选择是将数组的下标+1,代表检测过这个元素了。这个选择会影响最长子序列。
- 状态转化:因为dp[i]是数组下标为i时最长子序列,但不一定dp[i-1]一定小于dp[i]。所以这个状态转化为dp[i]=max(dp[i],(dp[0]…dp[i-1])+1)。
- 需要注意,更新dp数组的时机,当num[0]…num[i-1]之间有元素
- 最后的值是dp数组中最大的值,所以需要记录最大值作为返回值。
eg:
nums[j]
需要遍历0~i-1次dp数组才可以判断dp[i]的值。
这个状态转移是第二类。依赖比 i 小的 O(n) 个子问题
class Solution {
public:
int lengthOfLIS(vector& nums) {
//dp[i]代表数组下标为i时最长子序列
vectordp(nums.size(),1);
dp[0]=1;
int ret=1;
for(int i=1;i
for(int j=0;j
//因为dp[i]最大值不一定是最后值,所以每次都需要遍历一遍dp数组找最大值
if(nums[j]
dp[i]=max(dp[j]+1,dp[i]);
}
}
ret=max(ret,dp[i]);
}
return ret;
}
};
最长递增子序列的个数
LeetCode原题链接
这个题的思路与上题相同,但是需要返回最长递增的子序列个数。
首先,我们是按照递增的方式找的子序列,所以数列一定是递增的。
我们定义一个数组count,count[i]代表nums[i]元素之前的递增序列长度有多少个。
最后答案为count数组中长度是最长子序列的所有数组元素和。
int longest;//最长递增子序列 int ans;//最后答案 for(int i=0;iif (dp[i] == longest) { ans += counts[i]; } }
之所以这么设,是因为最长递增子序列可能在dp数组的任何一个位置。
其次在一问中我们可以知道
当dp[i]>dp[j]需要更新dp数组
dp[i]=max(dp[i],(dp[0]…dp[i-1])+1)
for(int i=1;ifor(int j=0;j //因为dp[i]最大值不一定是最后值,所以每次都需要遍历一遍dp数组找最大值 if(nums[j] //dp[i]=max(dp[j]+1,dp[i]); if(dp[j]>=dp[i]){ dp[i]=dp[j]+1 ; count[i]=count[j];//最长递增子序列发生改变,此时count[i]为最长递增子序列,其值和发生改变的上一个位置相同。 } else if(dp[j] + 1 == dp[i]){ //最长递增子序列长度没变化,但是序列发生变化,有count[j] 个额外的序列 count[i]+=count[j]; } } } ret=max(ret,dp[i]); }
- 如果这些序列比 length[i] 长,那么我们就知道我们有count[j] 个长度为 length 的序列。
- length[j] + 1 = length[i]说明在某个最长递增序列中,num[j]正好是num[i]的前一个元素。那么num[j]所对应的count[j]应该要累加到num[i]所对应的count[i]上
class Solution {
public:
int findNumberOfLIS(vector& nums) {
int size=nums.size();
if(size==0||size==1){
return size;
}
vectordp(size,1);
vectorcount(size,1);
dp[0]=1;
count[0]=1;
int maxlen=1;
for(int i=0;i
for(int j=0;j
if(nums[i]>nums[j]){
//dp[i]=max(dp[j]+1,dp[i]);
if(dp[j]+1==dp[i]){
count[i]+=count[j];
}
else if(dp[j]+1>=dp[i]){
count[i]=count[j];
dp[i]=dp[j]+1;
}
}
}
maxlen=max(maxlen,dp[i]);
}
int ans=0;
for(int i=0;i
if(dp[i]==maxlen){
ans+=count[i];
}
}
return ans;
}
};



