poj2823--Sliding Window--线段树||单调队列

Sliding Window
Time Limit: 12000MS Memory Limit: 65536K
Total Submissions: 33661 Accepted: 9985
Case Time Limit: 5000MS

Description

An array of size  n ≤ 10 6 is given to you. There is a sliding window of size  k which is moving from the very left of the array to the very right. You can only see the  k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example: 
The array is  [1 3 -1 -3 5 3 6 7], and  k is 3.
Window positionMinimum valueMaximum value
[1  3  -1] -3  5  3  6  7 -13
 1 [3  -1  -3] 5  3  6  7 -33
 1  3 [-1  -3  5] 3  6  7 -35
 1  3  -1 [-3  5  3] 6  7 -35
 1  3  -1  -3 [5  3  6] 7 36
 1  3  -1  -3  5 [3  6  7]37

Your task is to determine the maximum and minimum values in the sliding window at each position. 

Input

The input consists of two lines. The first line contains two integers  n and  k which are the lengths of the array and the sliding window. There are  n integers in the second line. 

Output

There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values. 

Sample Input

8 3
1 3 -1 -3 5 3 6 7

Sample Output

-1 -3 -3 -3 3 3
3 3 5 5 6 7

Source



方法一:线段树求区间最大最小值。。。尽管我看完后第一眼想不到= =!

很裸的诶,插入点是O(logN),查询区间也是O(logN)就不多说了= =!

代码如下,

#include <iostream>
#include <algorithm>
#include <cstdio>
#define mid ((l+r)>>1)
#define lson (root<<1)
#define rson (root<<1|1)
using namespace std;
const int maxnum=1e6+100;
struct Node
{
    int l,r;
    int val;
    int _max;
    int _min;
}tree[3*maxnum];

void build_tree(int l,int r,int root)
{
    tree[root].l=l;
    tree[root].r=r;
    if(l==r)
        return;
    else
    {
        build_tree(l,mid,lson);
        build_tree(mid+1,r,rson);
    }
}

void update_point(int value,int position,int root)
{
    int l=tree[root].l;
    int r=tree[root].r;
    if(l==r)
    {
        tree[root].val=value;
        tree[root]._max=value;
        tree[root]._min=value;
    }
    else
    {
        if(position<=mid) update_point(value,position,lson);
        else if(position>mid) update_point(value,position,rson);
        tree[root]._max=max(tree[lson]._max,tree[rson]._max);
        tree[root]._min=min(tree[lson]._min,tree[rson]._min);
    }
}

int max_ans;
int min_ans;
bool flag;

void query_interval(int ql,int qr,int root)
{
    int l=tree[root].l;
    int r=tree[root].r;
    if(l==ql&&r==qr)
    {
        if(flag)
        {
            max_ans=tree[root]._max;
            min_ans=tree[root]._min;
            flag=false;
        }
        else
        {
            max_ans=max(max_ans,tree[root]._max);
            min_ans=min(min_ans,tree[root]._min);
        }
    }
    else
    {
        if(qr<=mid)
        {
            query_interval(ql,qr,lson);
        }
        else if(ql>mid)
        {
            query_interval(ql,qr,rson);
        }
        else
        {
            query_interval(ql,mid,lson);
            query_interval(mid+1,qr,rson);
        }
    }
}
int ans1[maxnum];
int ans2[maxnum];
int main()
{
    //freopen("input.txt","r",stdin);
    int n,k;
    cin>>n>>k;
    if(k>n) k=n;
    build_tree(1,n,1);
    for(int i=1;i<=n;++i)
    {
        int temp;
        scanf("%d",&temp);
        update_point(temp,i,1);
    }
    for(int i=1;i<=n-k+1;++i)
    {
        flag=true;
        query_interval(i,i+k-1,1);
        ans1[i]=min_ans;
        ans2[i]=max_ans;
    }
    int i;
    for(i=1;i<=n-k;++i)
        printf("%d ",ans1[i]);
    printf("%d\n",ans1[i]);
    for(i=1;i<=n-k;++i)
        printf("%d ",ans2[i]);
    printf("%d\n",ans2[i]);
    return 0;
}

第二种是O(N)的维护一个单调队列

很详尽的解题分析!

•对于序列{3,2,4,6,8,8,10,1,3,6}
•我们首先看前5个数
•3,2,4,6,8最小的为2
•那么对于3这样的数,存在还有什么意义呢?
•我们只需要2,4,6,8就足够了
•下次比较2,4,6,8,8就可以了,前一个8也是没有意义的,只保留2,4,6,8,答案为2
•下次我们的2也不能再保留了,2出队,剩下4,6,8,10,答案是4
•再下一次,1加入,4出队,那么前面的6,8,10活着还有什么意义?(为什么)全部出队!答案为1
•这时我们队列中只剩下1了,下一次加入3,答案还是为1,最后加入6,答案不变。

•我们看到利用双端队列我们维护了一个单调递增的序列(想想为什么),每次答案都是队首元素。
•出队的条件只有两个:
•1.它的位置超过了我们所求区间的限制。(队列头出队)
•2.它的存在没有意义。(队列尾出队)
由于每个元素入队出队的次数不会超过n,时间复杂度O(n)


以下是代码,居然WA了一炮!初始化要牢记在心啊!写每一步程序要去思考啊,你又一点都不聪明!:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxnum=1e6+100;


struct queue
{
    int index;
    int value;
};


queue min_ans[maxnum];
queue max_ans[maxnum];
int a[maxnum];


int main()
{
    freopen("input.txt","r",stdin);
    int n,k;
    scanf("%d%d",&n,&k);
    if(k>n) k=n;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
    }
    int head=1,tail=1;
    min_ans[1].value=a[1];
    min_ans[1].index=1;
    max_ans[1].value=a[1];
    max_ans[1].index=1;
    for(int i=2;i<=k;++i)
    {
        while(head<=tail&&a[i]<=min_ans[tail].value)
        {
            --tail;
        }
        ++tail;
        min_ans[tail].value=a[i];
        min_ans[tail].index=i;
    }
    printf("%d",min_ans[head].value);
    for(int i=k+1;i<=n;++i)
    {
        if(min_ans[head].index<=i-k) ++head;  //进来啥都别想,一定要记得考虑出队情况!
        while(head<=tail&&a[i]<=min_ans[tail].value)
        {
            --tail;
        }
        ++tail;
        min_ans[tail].value=a[i];
        min_ans[tail].index=i;
        printf(" %d",min_ans[head].value);
    }
    printf("\n");


    head=tail=1;//初始化要牢记在心啊!
    for(int i=2;i<=k;++i)
    {
        while(head<=tail&&a[i]>=max_ans[tail].value)
        {
            --tail;
        }
        ++tail;
        max_ans[tail].value=a[i];
        max_ans[tail].index=i;
    }
    printf("%d",max_ans[head].value);
    for(int i=k+1;i<=n;++i)
    {
        if(max_ans[head].index<=i-k) ++head; //进来啥都别想,一定要记得考虑出队情况!
        while(head<=tail&&a[i]>=max_ans[tail].value)
        {
            --tail;
        }
        ++tail;
        max_ans[tail].value=a[i];
        max_ans[tail].index=i;
        printf(" %d",max_ans[head].value);
    }
    printf("\n");
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值