我觉得就像线性结构一样,具有一对一的邻接逻辑关系,简单来说就是n个数据元素的有序集合;
线性dp也就是在线性结构上的递推,所解决的问题也就像LIS和LCS一样。
LIS:最长上升子序列问题;
LCS:最长公共子序列问题。
线性动规也是一种动态规划,所以他的思路也是把多阶段问题划分成单阶段的子问题。例如LIS问题,你可以把他的长度划分成不同的阶段,只有两个字符时,只有三个字符时等…然后逐步递推,借助转移方程来进行转移,最后求出问题答案。
例如:1759:最长上升子序列
一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).
你的任务,就是对于给定的序列,求出最长上升子序列的长度。
输入:
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
输出:
最长上升子序列的长度。
样例输入:
7
1 7 3 5 9 4 8
样例输出:
4
1.具体问题具体分析。
2.划分阶段。
不同的长度阶段下有不同的状态;
3.确定状态变量。
i—控制长度变量;
j—当前第几个字符;
dp[i]表示以a[i]结尾的最长上升子序列的长度 。
4.建立各阶段状态变量的转移过程,确定状态转移方程。
边界处理:
dp[i]=1;
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
dp[i] | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
i=2时:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
dp[i] | 1 | 2 | 1 | 1 | 1 | 1 | 1 |
i=3时:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
dp[i] | 1 | 2 | 2 | 1 | 1 | 1 | 1 |
i=4时:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
dp[i] | 1 | 2 | 2 | 3 | 1 | 1 | 1 |
i=5时:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
dp[i] | 1 | 2 | 2 | 3 | 4 | 1 | 1 |
i=6时:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
dp[i] | 1 | 2 | 2 | 3 | 4 | 3 | 1 |
i=7时:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
dp[i] | 1 | 2 | 2 | 3 | 4 | 3 | 4 |
利用滚动数组来记录每个数值上出现的最大值;
所以我们通过表得出转移方程:
dp[i]=max(dp[i],dp[j]+1);
//转移方程;
具体是怎么得到的转移方程呢:
以第4项为例;
a[4]>a[1],a[4]可以接在a[1]后面,dp[4]=dp[1]+1=2;
a[4] a[4]>a[3],a[4]可以接在a[3]后面,dp[4]=dp[3]+1=3;
a[4]本身算一个上升子序列,dp[4]=1;
然后在其中取最大,dp[4]最后就会得到3;
5.确定终止条件。
由上表我们可以得到递推的终止条件j;
另外因为我们由上表得到最后一次dp[i]就是每个a[i]的最长上升子序列,所以我们要求全部最长的时候,应该要求dp的最大值;
代码如下:
int maxx=dp[1];
for(int i=2;i<=n;i++)
maxx=max(maxx,dp[i])
#include
#include
#define N 100001
int max(int a,int b)
{
if(a>=b)
return a;
return b;
}
int main()
{
int a[N],dp[N];
int n;
scanf("%d",&n);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i]=1;
}
//输入和边界处理
for(int i=2;i<=n;i++)
{
for(int j=1;j<i;j++)
if(a[j]<a[i])
dp[i]=max(dp[i],dp[j]+1);
}
//转移方程
int maxx=dp[1];
for(int i=2;i<=n;i++)
maxx=max(maxx,dp[i]);
printf("%d",maxx);
//因为最大值不一定在最后,所以需要在数组里寻找最大值
return 0;
}
for(int i=1;i<=len1;i++)
for(int j=1;j<=len2;j++)
if(a[i]==b[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
最优解可能不在最后,所以需要具体问题具体分析,可能需要进行一次比较,找到最大(小)解。同时线性动规一般是以数值区分问题的不同状态,所以找转移方程,也是在这方面去观察。