Sliding Window(poj-2823)

题目要求在宽度为k的滑动窗口中,找出每个位置上窗口内的最大和最小值。使用线段树可以高效地解决这个问题。原代码由于查询条件不准确导致超时,通过优化查询范围条件`if(L<=l&&r<=R) return sum[rt];`可以避免不必要的计算,提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Sliding Window
Time Limit: 12000MS Memory Limit: 65536K
Total Submissions: 67410 Accepted: 19122
Case Time Limit: 5000MS

Description

An array of size n ≤ 106 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

题意:一排n个数字在宽为k(就是说每次能看见连续的k个数字)的窗户依次向右移动一位,问每次从窗户看到的数字中的最大,最小的数是什么。

思路:用线段树查询[i,i+m-1]的最大,最小值。

比赛时打线段是模板超时,又没能想出怎么优化代码,还是对线段树理解存在1模糊,才不能根据题意灵活运用,做出相应的,变通及优化。下来有对线段树做了理解。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=1e6+10;

#define rson m+1,r,rt<<1|1
#define lson l,m,rt<<1
ll sum[N<<2],sum1[N<<2];
ll max(ll a,ll b)
{
    return a>b? a:b;
}
ll min(ll a,ll b)
{
    return a<b?a:b;
}
void PushUp(int rt)
{
    sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
    sum1[rt]=min(sum1[rt<<1],sum1[rt<<1|1]);
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        scanf("%I64d",&sum[rt]);
        sum1[rt]=sum[rt];
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
ll res=-inf;
ll res1=inf;
ll query1(int l,int r,int L,int R,int rt)
{
    if(L<=l&&r<=R) return sum[rt];
    int m=(l+r)>>1;
    if(R<=m) {res=max(res,query1(l,m,L,R,rt<<1));}
    else if(L>m) {res=max(res,query1(m+1,r,L,R,rt<<1|1));}
    else
    {
        res=max(res,query1(l,m,L,R,rt<<1));
        res=max(res,query1(m+1,r,L,R,rt<<1|1));
    }
    return res;
}
ll query2(int l,int r,int L,int R,int rt)
{

    if(L<=l&&r<=R) return sum1[rt];
    int m=(l+r)>>1;
    if(R<=m)res1=min(res1,query2(l,m,L,R,rt<<1));
    else if(L>m)res1=min(res1,query2(m+1,r,L,R,rt<<1|1));
    else
    {
        res1=min(res1,query2(l,m,L,R,rt<<1));
        res1=min(res1,query2(m+1,r,L,R,rt<<1|1));
    }
    return res1;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        memset(sum,0,sizeof(sum));
        memset(sum1,0,sizeof(sum1));
        build(1,n,1);
        for(int i=1;i<=n-m+1;i++)
        {
            res1=inf;
            if(i==1) printf("%I64d",query2(1,n,i,i+m-1,1));
            else printf(" %I64d",query2(1,n,i,i+m-1,1));
        }
        printf("\n");
        for(int i=1;i<=n-m+1;i++)
        {
            res=-inf;
            if(i==1) printf("%I64d",query1(1,n,i,i+m-1,1));
            else printf(" %I64d",query1(1,n,i,i+m-1,1));
        }
        printf("\n");
    }
    return 0;
}
  if(L<=l&&r<=R) return sum[rt];

之前我写的 return条件是     if(L==l&&r==R) return sum1[rt];  和  

                               if(l==r) {res1=min(res1,sum1[rt]);return res1;}超时了。

对比了俩个过程:



所以知道了if(L<=l&&r<=R) return sum[rt];还是可以省去了很多没必要得计算。如在[1,10]中找[1,6].第一次找到得[1,5]的sum[2]就不用在往下找了。

 if(R<=m)res1=min(res1,query2(l,m,L,R,rt<<1));
    else if(L>m)res1=min(res1,query2(m+1,r,L,R,rt<<1|1));

查询中这个范围的由来:为什么是R<=m,L>m不是R<=m,L>=m,也不是R<m,L>m.?在[5,8]里找[5,6],m=6;就不用往[5,8]的右儿子找了。在[1,4]里找[2,4],m=2,2属于左儿子,还需要在[1,4]的左儿子中找。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值