Time Limit: 12000MS | Memory Limit: 65536K | |
Total Submissions: 67410 | Accepted: 19122 | |
Case Time Limit: 5000MS |
Description
The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window position | Minimum value | Maximum value |
---|---|---|
[1 3 -1] -3 5 3 6 7 | -1 | 3 |
1 [3 -1 -3] 5 3 6 7 | -3 | 3 |
1 3 [-1 -3 5] 3 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | -3 | 5 |
1 3 -1 -3 [5 3 6] 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7] | 3 | 7 |
Your task is to determine the maximum and minimum values in the sliding window at each position.
Input
Output
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]的左儿子中找。