Window_纪中1326_水_单调队列

99 篇文章 0 订阅
55 篇文章 0 订阅

Description

  给你一个长度为N的数组,一个长为K的滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,如下表:
  
  你的任务是找出窗口在各位置时的max value,min value.

Input

  第1行n,k,第2行为长度为n的数组

Output

  2行,第1行每个位置的min value,第2行每个位置的max value

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

Hint

数据范围:
  20%: n<=500; 50%: n<=100000;
  100%: n<=1000000;

思路

原本想着是显而易见的线段树查找最大值,结果爆内存/(ㄒoㄒ)/~~
pige优美的暴力ac后才发现是水题
能暴力就暴力,做对那些脑残题
真理

方法I:

每次判断当前框里的最大值和最小值是否出了框,没有出框就与最大值和最小值比较,若出框了就在框里寻找最大值和最小值

方法II:

经大神指导后习得单调队列做法,然而Pascal TLE
于是乎我就手鲁出c++来
根据单调队列的性质我们可以很方便地求出最大值与最小值,注意查询是否出框

代码/pas

var
  n,m,max,min,i,j:Longint;
  a,t,r:array[0..1000000]of longint;
  max_s,min_s:longint;
function findmax(x:longint):longint;
var
  i:Longint;
begin
  findmax:=-maxlongint;
  for i:=x to x+m-1 do
  if findmax<a[i] then
  begin
    findmax:=a[i];
    max_s:=i;
  end;
end;
function findmin(x:Longint):Longint;
var
  i:longint;
begin
  findmin:=maxlongint;
  for i:=x to x+m-1 do
  if findmin>a[i] then
  begin
    findmin:=a[i];
    min_s:=i;
  end;
end;
begin
  readln(n,m);
  for i:=1 to n do
  read(a[i]);
  max:=findmax(1);
  min:=findmin(1);
  inc(t[0]);
  t[t[0]]:=min;
  r[t[0]]:=max;
  for i:=2 to n-m+1 do
  begin
    if max_s>=i then
    begin
      if a[i+m-1]>max then
      begin
        max:=a[i+m-1];
        max_s:=i+m-1;
      end;
    end
    else
    max:=findmax(i);
    if min_s>=i then
    begin
      if a[i+m-1]<min then
      begin
        min:=a[i+m-1];
        min_s:=i+m-1;
      end;
    end
    else
    min:=findmin(i);
    inc(t[0]);
    t[t[0]]:=min;
    r[t[0]]:=max;
  end;
  for i:=1 to t[0] do
  write(t[i],' ');
  writeln;
  for i:=1 to t[0] do
  write(r[i],' ');
end.

代码/c++

#include <stdio.h>
struct rec
{
    int x,r;
};
rec t[1000000]={0};
rec r[1000000]={0};
int a[1000000]={0};
int mx[1000000]={0};
int mn[1000000]={0};
int maxint=0xffffffff;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int p=0;
    int q=0;
    for (int i=1;i<=n;i++)
    {
        t[i].x=-maxint;
        r[i].x=maxint;
    }
    for (int i=1;i<=n;i++)
    {
        t[++p].x=a[i];
        t[p].r=i;
        while (p>1&&t[p].x>t[p-1].x) t[p-1]=t[p--];
        while (p>1&&t[p].r-t[1].r>=m)
        {
            int v=2;
            while (v<p) t[v-1]=t[v++];
            p--;
        }
        r[++q].x=a[i];
        r[q].r=i;
        while (q>1&&r[q].x<r[q-1].x) r[q-1]=r[q--];
        while (q>1&&r[q].r-r[1].r>=m)
        {
            int v=2;
            while (v<q) r[v-1]=r[v++];
            q--;
        }
        mx[i]=t[1].x;
        mn[i]=r[1].x;
    }
    for (int i=m;i<=n;i++) printf("%d ",mn[i]);
    printf("\n");
    for (int i=m;i<=n;i++) printf("%d ",mx[i]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值