题目链接:
HDU 3530 Subsequence
题意:
给一个长度为
n
的数组,求最大的区间长度,并且这个区间满足最大值与最小值之差
数据范围:
n≤105,m,k∈[0,106]
分析:
因为是在做单调队列的题,所以往单调队列上想可能容易点。
首先我们考虑以
data[i]
结尾的最长的满足条件的区间。因为我们需要考虑区间最大值和区间最小值,不妨维护两个单调队列:单调非递增队列
dec
和单调非递减队列
inc
,用
data[i]
的大小和队列尾的元素大小比较来维护队列的单调性。我们想要寻找的区间一定要满足最大值减去最小值之差
diff≤k
,所以我们同时也要调整队列首。但是我们要不要根据
diff≥m
,来调整队列首呢?实际上是不需要的,而且调整是错误的。因为尽管我们当前两个队列的差值
diff<m
,那必然是因为最大值太小或者最小值太大,但是我们可以通过后面的值比较大来提升最大值或者后面的值比较小来减小最小值使得
diff≥m
,如果调整当前
diff
使其
diff≥m
的话,相当于减少了区间长度,甚至最终会无解,越界什么的。
m
的正确作用应当是来判断当前两个队列中的元素能否形成满足题意的区间,如果可以的话就更新
时间复杂度:
O(n)
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <climits>
using namespace std;
const int MAX_N = 100010;
int n, m, k, head_dec, tail_dec, head_inc, tail_inc;
int dec[MAX_N], inc[MAX_N], data[MAX_N];
int main()
{
while (~scanf("%d%d%d", &n, &m, &k)) {
head_dec = tail_dec = head_inc = tail_dec = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", &data[i]);
}
if(m > k) {
printf("0\n");
continue;
}
memset(dec, 0, sizeof(dec));
memset(inc, 0, sizeof(inc));
int ans = 0, diff, pre = 0;
for (int i = 0; i < n; ++i) {
while (head_inc != tail_inc && data[i] < data[inc[tail_inc - 1]]) --tail_inc;
inc[tail_inc++] = i;
while (head_dec != tail_dec && data[i] > data[dec[tail_dec - 1]]) --tail_dec;
dec[tail_dec++] = i;
while(1) {
diff = data[dec[head_dec]] - data[inc[head_inc]];
if (diff > k) {
if (dec[head_dec] < inc[head_inc]) {
pre = dec[head_dec] + 1;
head_dec++;
} else {
pre = inc[head_inc] + 1;
head_inc++;
}
} else break;
}
diff = data[dec[head_dec]] - data[inc[head_inc]];
if(diff >= m) ans = max(ans, i - pre + 1);
// printf("i = %d head_dec = %d head_inc = %d ans = %d\n", i, dec[head_dec], inc[head_inc], ans);
// printf("Max = %d Min = %d\n", data[dec[head_dec]], data[inc[head_inc]]);
}
printf("%d\n", ans);
}
return 0;
}