UVA 1471 - Defense Lines(扫描+二分)

UVA 1471 - Defense Lines

题目链接

题意:给定一个序列,要求删去一个连续子序列后,得到的序列有一个最长的连续递增序列,输出最长连续递增序列长度

思路:先左右扫描一遍,把每个位置往左和往右的最大长度记录下来,然后在从左往右扫描一遍,开一个数组Min用来记录长度i的序列,最后一位的最小值,这个序列是满足单调性的,因为递增序列肯定是1,2,3,4...这样不断往上加的,如果遇到一个a[i]比较小的,就维护Min相应长度下的值,这样在这个单调递增的序列中,每次就可以二分找出最后一位小于a[i],然后和当前位置往右的序列长度拼接起来,得到一个长度,记录下最大值就是答案

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 200005;

int t, n, a[N], left[N], right[N], Min[N];

void init() {
    scanf("%d", &n);
    left[0] = 1; right[n - 1] = 1;
    for (int i = 0; i < n; i++) {
	scanf("%d", &a[i]);
	if (i) {
	    left[i] = 1;
	    if (a[i] > a[i - 1]) left[i] += left[i - 1];
	}
    }
    for (int i = n - 2; i >= 0; i--) {
	right[i] = 1;
	if (a[i] < a[i + 1]) right[i] += right[i + 1];
    }
}

int solve() {
    int ans = 0;
    memset(Min, INF, sizeof(Min));
    for (int i = 0; i < n; i++) {
	int len = lower_bound(Min + 1, Min + 1 + n, a[i]) - Min;
	ans = max(ans, right[i] + len - 1);
	Min[left[i]] = min(Min[left[i]], a[i]);
    }
    return ans;
}

int main() {
    scanf("%d", &t);
    while (t--) {
	init();
	printf("%d\n", solve());
    }
    return 0;
}


你可能感兴趣的:(UVA 1471 - Defense Lines(扫描+二分))