题目链接:http://codeforces.com/problemset/problem/447/C
题目意思:给出 一个 包含 n 个数的序列你,从中需要找出这个序列的最长子串,满足在里面只修改其中一个元素,使得这个子串的元素严格递增,求出这个长度是多少。
以为是DP题(它的分类确实是DP题),无从下手。看了下别人写的,有些部分不太明白;根据自己的理解再结合一些别人的,加了些特判,总算过了^_^,我好像觉得我的做法不像是用了DP思想咯........
总体思路就是:设两个数组,分别为inc_left[] 和 inc_right[],顾名思义,对于第 i 个元素,inc_left[i]就是从左到右扫描序列,到达这个inc_left[i]元素的时候,它的递增长度是多少。举个例子,对于test 1 中的 7 2 3 1 5 6
inc_left[i]为: 1 1 2 1 2 3
对于6这个数来说,inc_left[6] = 3 是因为 1 5 6 严格递增。而为什么有些inc_left[i] = 1,是因为前面加上自己,不构成递增序列。inc_right[]依次类推。
这两个数组结合起来就可以求出 2 3 1 5 6 的情况了,即对于某个a[i],只改变a[i]这个数,使得以这个a[i] 为原点,左右扩散起来,能够构成的最长递增序列。
要注意一些特判,例如,当n = 1 时直接输出1;如果输入序列本来已经严格递增,直接输出n。最后一种情况就是,1 1 这种,只改变其中一个元素,答案为2,代码中的
ans = max(ans, max(inc_left[i]+1, inc_right[i]+1)); 就是为了应对这种情况的。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 using namespace std; 6 7 const int N = 1e5 + 5; 8 int a[N], inc_left[N], inc_right[N]; 9 10 int main() 11 { 12 int n; 13 while (scanf("%d", &n) != EOF) 14 { 15 memset(a, 0, sizeof(a)); 16 for (int i = 1; i <= n; i++) 17 scanf("%d", &a[i]); 18 if (n == 1) 19 printf("1 "); 20 else 21 { 22 int ans = 0; 23 inc_left[1] = 1; 24 for (int i = 2; i <= n; i++) 25 { 26 inc_left[i] = (a[i] > a[i-1] ? inc_left[i-1]+1 : 1); 27 ans = max(ans, inc_left[i]); 28 } 29 inc_right[n] = 1; 30 for (int i = n-1; i >= 1; i--) 31 { 32 inc_right[i] = (a[i+1] > a[i] ? inc_right[i+1]+1 : 1); 33 ans = max(ans, inc_right[i]); 34 } 35 if (ans == n) // 处理序列本来就是递增情况 36 printf("%d ", n); 37 else 38 { 39 for (int i = 1; i <= n; i++) 40 { 41 if (a[i+1] > a[i-1]+1) // 这两种情况需排除:1 ? 2 或 1 ? 1,可以更改的至少需要满足1 ? 3 42 ans = max(ans, inc_right[i+1]+inc_left[i-1]+1); 43 else 44 ans = max(ans, max(inc_left[i]+1, inc_right[i]+1)); // 处理 1 1的情况,只能更改其中一个 45 } 46 printf("%d ", ans); 47 } 48 } 49 } 50 return 0; 51 }