时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
给出N个整数,和一个长度值Len,要求在这N个整数中每个长度为Len的连续一段数中的最大值。
例如:N=8,Len=3,8个整数是:2 5 1 1 2 4 7 1。答案是 5 5 2 4 7 7 。
解释:
2 5 1的最大值是5
5 1 1的最大值是5
1 1 2的最大值是2
1 2 4的最大值是4
2 4 7的最大值是7
4 7 1的最大值是7
输入
第一行2个正整数:N,Len。N范围[2…100000],Len范围[2…N]
第二行:N个正整数,每个数范围[1…1000000000]。
输出
一行,N-Len+1个整数。
样例输入 Copy
4 3
7 2 1 4
样例输出 Copy
7 4
题意很简单,就是求[i,i+len]内所有元素的最大值,其中i的范围为[1,n-len]。
暴力显然不行。我首先采用优先队列(priority queue)+队列(保存每个最大值)来做,结果发现会打乱序列原有的顺序,然后考虑dp,仍然没思路。最后百度了一下,可以用双端队列(deque)来做。
双端队列也是一种容器,它可以在队首进行插入(push_front())和删除(pop_front())操作,也可以在队尾进行插入(push_back())和删除(pop_back())操作,还可以访问队首的元素(front())。结合题意,我们可以维护一个递减队列,这样访问的队首元素恰好是区间内的最大值。
这个题的关键是如何插入和删除元素。
插入:为了保证单调队列的递减性,我们在插入元素xx(struct x类型)的时候,要将队尾的元素和xx的a成员(表示该元素的数据)比较,如果队尾的元素不大于a,则删除队尾的元素,然后继续将新的队尾的元素与a比较,直到队尾的元素大于a,这个时候我们才将xx插入到队尾。
删除:由于我们只需要保存len个元素中的最大值,所以当队首的元素的下标小于等于i-len的时候,就说明队首的元素对于求最大值已经没有意义了。(注意不是每经过一个循环后就删除队首元素,而是要根据队首元素的下标来确定,因为有时单调队列中的元素不一定是len个)所以当index<=i-len时,将队首元素删除。
模拟一下样例:
8 3
2 5 1 1 2 4 7 1
前3个元素:2 5 1
i=1,2入队
i=2,2出队,5入队,队中元素为5
i=3,1入队,队中元素为5 1
i=4,1入队,队中元素为5 1 1
i=5,队首元素5的下标为2,不在范围内,5出队;2>1,1出队,1出队,2入队,队中元素为2
i=6,队首元素为2,2<4,2出队,4入队,队中元素为4
i=7,队首元素为4,4<7,4出队,7入队,队中元素为7
i=8,队首元素为7,7>1,1入队,队中元素为7,1
显然i>=3时每步的队首元素就是所求的最大值(5 5 2 4 7 7)。
这个题与我之前写的单调栈的题解有相似的地方。
#include<cstdio>
#include<queue>
#include<deque>
using namespace std;
struct x
{
int index;
int a;
};
struct x xx[100005];
queue<int>ans;
deque<struct x>b;
int main()
{
int n,len,i;
scanf("%d %d",&n,&len);
for(i=1;i<=n;i++)
{
scanf("%d",&xx[i].a);
xx[i].index=i;
}
for(i=1;i<=len;i++)//前len个元素单独处理
{
while(!b.empty()&&b.back().a<=xx[i].a)b.pop_back();
b.push_back(xx[i]);
}
ans.push(b.front().a);//队首元素放入队列,等待最后输出
for(i=len+1;i<=n;i++)
{
while(!b.empty()&&b.front().index<=i-len)b.pop_front();
while(!b.empty()&&b.back().a<=xx[i].a)b.pop_back();
b.push_back(xx[i]);
ans.push(b.front().a);//队首元素放入队列,等待最后输出
}
printf("%d",ans.front());//注意最后一个数字后面没有空格
ans.pop();
while(!ans.empty())
{
printf(" %d",ans.front());
ans.pop();
}
return 0;
}
/**************************************************************
Language: C++
Result: 正确
Time:37 ms
Memory:2860 kb
****************************************************************/
这个题也可以用ST表来做。
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
int st_table[100005][20];
int a[100005];
int Log[100005];
queue<int>ans;
int main()
{
int n,i,j,len;
scanf("%d %d",&n,&len);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<=n;i++)st_table[i][0]=a[i];
Log[1]=0;
for(i=2;i<=n;i++)Log[i]=Log[i/2]+1;
for(j=1;(1<<j)<=n;j++)
{
for(i=1;i+(1<<j)-1<=n;i++)
{
st_table[i][j]=max(st_table[i][j-1],st_table[i+(1<<(j-1))][j-1]);//注意是i+(1<<(j-1)),不是i+(1<<j)
}
}
for(i=1;i<=n-len+1;i++)
{
ans.push(max(st_table[i][Log[len]],st_table[i+len-(1<<Log[len])][Log[len]]));//区间长度别忘了+1
}
printf("%d",ans.front());
ans.pop();
while(!ans.empty())
{
printf(" %d",ans.front());
ans.pop();
}
return 0;
}
/**************************************************************
Language: C++
Result: 正确
Time:49 ms
Memory:10664 kb
****************************************************************/