活动地址:优快云21天学习挑战赛
文章目录
前言
这是鄙人第一次正儿八经的教学文章,还是蛮紧张的,不懂可以留言询问。本文可能适合稍微回些动态规划的人。经典例题还会继续补充。
1、介绍
线性DP是动态规划问题中的一类问题,指状态之间有线性关系的动态规划问题。尤其是最长上升子序列问题,最长公共子序列方案数等问题。其实线性dp无论是几维的,都是要从一个地方入手,以某确定方向获得整个状态空间转移、拓展。每个状态都保留子问题最优解。
2、例题:最长上升子序列问题
题目描述:给定n个数ai,求数值单调递增的子序列的最长长度为多少?
最长上升子序列(一)_牛客题霸_牛客网 (nowcoder.com)
分析:不妨先自己设几个数: 3 1 3 2 4 3。我们可以观察到最长的是1 2 4或1 2 3或1 3 4,大小为3。我们可以发现可以存储以a[i]结尾的状态(长度)。令f【i】表示以a[i]为结尾的最长上升自序列的长度(初始均为1,本身一个数也是序列),从第一个3开始可以获得f:1 1 1 1 2 1,从1开始可以获得f:1 1 2 2 3 3......可以找出状态转移方程:f[i]=max(f[i],f[j]+1)。(可以了解一下闫氏dp法)
代码:
#include<iostream>
using namespace std;
int main()
{
int a[1010],n,res;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int f[1010];
for(int i=1;i<=n;i++)
{
f[i]=1;
for(int j=1;j<=i;j++)//对比一下之前,看看能不能更新最大值。
//我们要保证每一步做完之后,f[i]中保留的都是以a[i]结尾的最大长度。
{
if(a[j]<a[i])
f[i]=max(f[i],f[j]+1);
}
res=max(res,f[i]);
}
printf("%d",res);
}
此时,你会发现时间复杂度为o(n2),可用树状数组维护(复杂度为nlog(n))。奈何本人学艺不精,唉。
3、例题:最长子序列方案数量问题
673. 最长递增子序列的个数 - 力扣(LeetCode)
分析:这个题我们需要用到两个状态,故开两个数组,一个存以a【i】结尾的最长子序列长度,一个存以a【i】结尾的最长子序列个数。由于我们要更新g[i],f[i]还是分类进行处理。
class Solution {
public:
int findNumberOfLIS(vector<int>& nums) {
int n=nums.size();
int res=0,maxn=1 ;
int f[2010];//与上面的f含义相同
int g[2010];//以a【i】结尾的最长子序列的个数。
f[0]=g[0]=1;
for(int i=1;i<n;i++)
{
f[i]=1,g[i]=1;
for(int j=0;j<i;j++)
{
if(nums[j]<nums[i])//仅当前比后小时处理
{
if(f[j]+1>f[i])//当前最长长度不是最大//j比i小f[j]表示以a[j]结尾最长子序列的个数。+1才是当前长度。
{
f[i]=f[j]+1;
g[i]=g[j];//长度变了,个数自然也变了
}
else if(f[j]+1==f[i])//两个以f[j]结尾的一样长,个数相加
g[i]+=g[j];
}
}
if(f[i]>maxn)//求最大的长度
maxn=f[i];
}
for(int i=0;i<n;i++)
{ if(f[i]==maxn)
res+=g[i];}
return res;
}
};