线性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;
}

总结: 这题很锻炼思维。

相关推荐
Felven1 小时前
B. Fair Numbers
数据结构·算法
人道领域2 小时前
【LeetCode刷题日记】93.复原IP地址
java·开发语言·算法·leetcode
jarreyer2 小时前
【算法记录1】模型训练问题
算法
Felven2 小时前
D. Friends and the Restaurant
算法
想吃火锅10052 小时前
【leetcode】165.比较版本号js
javascript·算法·leetcode
San813_LDD2 小时前
[量化]《浮点数比较的艺术:从内存布局到极致性能优化》
网络·算法
ysu_03142 小时前
leetcode数据结构与算法1~4
c语言·数据结构·学习·算法·leetcode
小欣加油2 小时前
leetcode2574 左右元素和的差值
数据结构·c++·算法·leetcode·职场和发展
PH = 72 小时前
动态规划-求最优解-自底向上
算法·动态规划