线性dp-LIS题目4(A Twisty Movement)

题目链接

1 解题思路

1.1 没有翻转的情况

这道题我们先思考一下,如果没有翻转这个条件的情况。这时,我们可以拆分成两种情况,一种是最长不下降子序列只有1,另一种是最长不下降子序列有1也可以有2,如果当前的数字 a i ai ai是1就只能接在第一种情况后面;如果当前数字 a i ai ai是2,可以接在最长不下降子序列有1也可以有2的序列后面。

我们记最长不下降子序列只有1的子序列长度为 d p 1 i dp1i dp1i,最长不下降子序列有1也有2的子序列长度为 d p 2 i dp2i dp2i。初始化所有 d p 1 dp1 dp1和 d p 2 dp2 dp2的元素为1,只有自己时,长度为1。

得到状态转移方程: d p 1 i = d p 1 i − 1 + ( a i = = 1 ) dp1i=dp1i-1+(ai==1) dp1i=dp1i−1+(ai==1), d p 2 i = m a x ( d p 1 i , d p 2 i − 1 + ( a i = = 2 ) ) dp2i=max(dp1i,dp2i-1+(ai==2)) dp2i=max(dp1i,dp2i−1+(ai==2))

注意: 在这里 d p 2 dp2 dp2不是必须接2,可能比较容易错写成: d p 2 i = m a x ( d p 1 i , d p 2 i − 1 ) + ( a i = = 2 ) dp2i=max(dp1i,dp2i-1)+(ai==2) dp2i=max(dp1i,dp2i−1)+(ai==2)。这里蕴含了贪心的思想。如果dp1的纯1序列长度比dp2长,后面再来 1 或者 2 都能合法接上,选择自由度最高。如果这时还要硬选2,按照不下降规则,末尾是 2 之后再也不能接 1,一旦后面出现 1,这条路线直接 "走死",灵活性极低。所以, d p 2 i dp2i dp2i的可以允许有2,但也可以不选2。最后,输出答案 d p 2 n dp2n dp2n

1.2 加入翻转的情况

如果加入翻转,我们再定义一个 d p 3 dp3 dp3, d p 3 dp3 dp3的更新在更新完 d p 2 dp2 dp2之后。一般来说,遇到 a i = 1 ai=1 ai=1的时候会反转,故 d p 3 i = m a x ( d p 2 i , d p 3 i − 1 + ( a i = = 1 ) ) dp3i=max(dp2i,dp3i-1+(ai==1)) dp3i=max(dp2i,dp3i−1+(ai==1)),同样也是贪心的思想,如果 d p 2 dp2 dp2长度更长,就不浪费这次翻转机会,后续元素要反转可以在 d p 2 dp2 dp2指向的子序列后面。如果 a i = 1 ai=1 ai=1且 d p 3 i − 1 + 1 dp3i-1+1 dp3i−1+1更大,这样意味着翻转区间右端点延长到 i i i这样得到的不下降子序列更长,那选择翻转。翻转区间左端点在有1也有2的序列里面,一般是第一个在2之后出现的1所在的下标。所以 d p 3 dp3 dp3的主要作用是看右区间是不是要延展到 i i i。

翻转之后,后面的元素可以接在最长不下降序列后面,比如翻转后如果当前元素 a i = 2 ai=2 ai=2,就可以接上去。如果 a i = 1 ai=1 ai=1,如果最长不下降序列全是1,那 d p 1 dp1 dp1那里已经累计过了,会继承到 d p 3 dp3 dp3;如果最长不下降子序列有1也有2,那么翻转后的最长不下降子序列最后一个是2,接不上。故我们再开一个 d p 4 dp4 dp4, d p i = m a x ( d p 3 i , d p 4 i − 1 + ( a i = = 2 ) ) dpi=max(dp3i,dp4i-1+(ai==2)) dpi=max(dp3i,dp4i−1+(ai==2))。最后答案输出 d p 4 n dp4n dp4n

更新的核心代码

bash 复制代码
for(int i=1;i<=n;i++){
	dp1[i]=dp1[i-1]+(a[i]==1);
    dp2[i]=max(dp1[i],dp2[i-1]+(a[i]==2));
    dp3[i]=max(dp2[i],dp3[i-1]+(a[i]==1));
    dp4[i]=max(dp3[i],dp4[i-1]+(a[i]==2));
}
1.3 空间复杂度优化

由于 d p dp dp值的更新只有上一次有关,和前面的无关,我们实际上可以用一个 d p dp dp数组存储, d p 0 dp0 dp0, d p 1 dp1 dp1, d p 2 dp2 dp2, d p 3 dp3 dp3分别存原来 d p 1 i dp1i dp1i, d p 2 i dp2i dp2i, d p 3 i dp3i dp3i, d p 4 i dp4i dp4i的值。这样空间复杂度就大大优化了。

2 AC Code

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,x,dp[4];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x;
		dp[0]=dp[0]+(x==1);
		dp[1]=max(dp[0],dp[1]+(x==2));
		dp[2]=max(dp[1],dp[2]+(x==1));
		dp[3]=max(dp[2],dp[3]+(x==2));
	}
	cout<<dp[3];
  return 0;
}

总结: 这题很锻炼思维。

相关推荐
小小晓.2 分钟前
C++小白记:vector
开发语言·c++·算法
tkevinjd4 分钟前
力扣72-编辑距离
算法·leetcode·职场和发展
小刘学技术13 分钟前
AI人工智能决策树分类器:原理、实现与应用
开发语言·人工智能·python·算法·决策树·机器学习·数据挖掘
caimouse1 小时前
mm学习笔记_04:VAD树算法与地址空间分配
笔记·学习·算法·reactos
abcy0712131 小时前
flink窗口类型
开发语言·python·算法
什巳11 小时前
JAVA练习309- 二叉树的层序遍历
java·数据结构·算法·leetcode
什巳14 小时前
JAVA练习306- 翻转二叉树
java·数据结构·算法·leetcode
smj2302_7968265214 小时前
解决leetcode第3989题网格中保持一致的最大列数
python·算法·leetcode
巧克力男孩dd15 小时前
Python超典型练习题(第一次作业)
开发语言·python·算法
爱刷碗的苏泓舒15 小时前
平方根信息滤波:矩阵推导及 GNSS 参数估计应用
线性代数·算法·矩阵·gnss·参数估计·测量平差·平方根信息滤波