题意:给定一个长度为n的数组a,你至多可以删去其中的一个元素,找出最长的连续(严格)上升子序列的长度。
分析:读完题之后可以发现这道题和模板:连续上升子序列很相像,首先可以确定方向是dp:状态是当前的位置,目标是连续(严格)上升子序列的长度,即dp[i]表示以a[i]为结尾的最长的连续(严格)上升子序列的长度。但是,这道题不同于模板的地方就在于题目中给定了一个条件:至多可以删去其中的一个元素,乍一看似乎很难再做状态转移,但是不妨将删去这件事本身也引入状态转移中来。
不难想到“已经删去一个元素”的状态一定是由“没有删去元素”的状态转移过来的,我们可以使用dp第二维来表示是否已经删去了一个元素,即dp[i][0]表示在没有删去一个元素的前提下以a[i]为结尾的最长(严格)连续上升子序列的最长长度,而dp[i][1]则是以已经山区了一个元素为前提下的最长长度。想到这里,便可以开始推状态转移方程:
首先第一步,先不考虑删去元素的状态转移,dp[i][0]这个状态一定是由dp[i-1][0]转移而来的,即如果a[i] > a[i-1],则dp[i][0] = dp[i-1][0]+1;如果第二维=1,同样也不难想到,在这种情况下也是直接由dp[i-1][1]+1转移而来即可。
当a[i] > a[i-1]时,dp[i][0 or 1] = max(dp[i][0],dp[i-1][0 or 1]+1);
第二步:考虑删去元素的情况。倘若我们现在想要得到dp[i],考虑删去a[i-1],这样删去的时候,在从左到右的递推过程中就可以把所有的情况都覆盖到,而不会漏解或重复运算,并且删去a[i-1]使得dp[i]变化的条件是a[i] > a[i-2],也就是当前这一位数比它之前的之前一位数还要大时,删去它之前的数,可能会导致它的最长长度有所变化,其中就可能导致最大长度变大,因此,当a[i] > a[i-2]时,有:dp[i][1] 由 dp[i-2][0] + 1 转移而来。
当a[i] > a[i-2]时,dp[i][1] = max(dp[i][1],dp[i-2][0]+1);
那么综合前两步,不难得出状态转移方程,递推完方程之后,每一步更新结果即可。
代码:
#include <iostream> #include <algorithm> using namespace std; const int N = 200005; int dp[N][2],a[N]; int main() { int n;cin >> n; for(int i = 1;i <= n;++i) cin >> a[i]; dp[1][0] = 1; int res = -1; for(int i = 2;i <= n;++i) { dp[i][0] = 1;dp[i][1] = 1; if(a[i] > a[i-1]) { dp[i][0] = max(dp[i-1][0] + 1,dp[i][0]); dp[i][1] = max(dp[i-1][1] + 1,dp[i][1]); } if(a[i] > a[i-2]) dp[i][1] = max(dp[i-2][0] + 1,dp[i][1]); res = max(res,max(dp[i][0],dp[i][1])); } cout << res; return 0; }