线性动规(线性dp)
一.什么叫线性dp
我觉得就像线性结构一样,具有一对一的邻接逻辑关系,简单来说就是n个数据元素的有序集合;
线性dp也就是在线性结构上的递推,所解决的问题也就像LIS和LCS一样。
LIS:最长上升子序列问题;
LCS:最长公共子序列问题。
二.线性dp的思路
线性动规也是一种动态规划,所以他的思路也是把多阶段问题划分成单阶段的子问题。例如LIS问题,你可以把他的长度划分成不同的阶段,只有两个字符时,只有三个字符时等…然后逐步递推,借助转移方程来进行转移,最后求出问题答案。
三.线性dp的例题详解
例如:1759:最长上升子序列
(1)题目要求:
一个数的序列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).
你的任务,就是对于给定的序列,求出最长上升子序列的长度。
(2)输入与输出:
输入:
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
输出:
最长上升子序列的长度。
样例输入:
7
1 7 3 5 9 4 8
样例输出:
4
(3)解析
1.具体问题具体分析。
2.划分阶段。
不同的长度阶段下有不同的状态;
3.确定状态变量。
i—控制长度变量;
j—当前第几个字符;
dp[i]表示以a[i]结尾的最长上升子序列的长度 。
4.建立各阶段状态变量的转移过程,确定状态转移方程。
1 | 7 | 3 | 5 | 9 | 4 | 8 | |
---|---|---|---|---|---|---|---|
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 2 | 0 | 0 | 0 | 0 | 0 |
3 | 1 | 2 | 2 | 0 | 0 | 0 | 0 |
4 | 1 | 2 | 2 | 3 | 0 | 0 | 0 |
5 | 1 | 2 | 2 | 3 | 4 | 0 | 0 |
6 | 1 | 2 | 2 | 3 | 4 | 3 | 0 |
7 | 1 | 2 | 2 | 3 | 4 | 3 | 4 |
所以我们通过表得出转移方程:
dp[i]=max(dp[i],dp[j]+1);
//转移方程;
5.确定终止条件。
由上表我们可以得到递推的终止条件j<i
;
另外因为我们由上表得到最后一次dp[i]就是每个a[i]的最长上升子序列,所以我们要求全部最长的时候,应该要求dp的最大值;
代码如下:
int maxx=dp[1];
for(int i=2;i<=n;i++)
maxx=max(maxx,dp[i])
四.完整代码
#include<stdio.h>
#include<string.h>
#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[1]=1;
//把第一次填表简化为dp[1]=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;
}