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;
}
总结: 这题很锻炼思维。