单调队列的应用

来源:互联网 发布:error 1130 mysql 编辑:程序博客网 时间:2024/05/22 15:35

一、

描述
给定一个长度为n(n<=10^6)的数组。有一个大小为k的滑动窗口从数组的最左端移动到最右端。你可以看到窗口中的k个数字。窗口每次向右滑动一个数字的距离。
下面是一个例子:
数组是 [1 3 -1 -3 5 3 6 7], k = 3。

窗口位置最小值最大值[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


你的任务是得到滑动窗口在每个位置时的最大值和最小值。


输入
输入包括两行。
第一行包括n和k,分别表示数组的长度和窗口的大小。
第二行包括n个数字。
输出
输出包括两行。
第一行包括窗口从左至右移动的每个位置的最小值。
第二行包括窗口从左至右移动的每个位置的最大值。
样例输入
8 31 3 -1 -3 5 3 6 7
样例输出
-1 -3 -3 -3 3 33 3 5 5 6 7


构造一个单调队列并进行维护。以求最小值为例,构造一个队列存储元素的下标,每有一个新的元素,如果队列为空直接插入,若非空,则从队尾开始比较将大于插入元素的都清除,然后插入新元素作为备选。(新元素的下标比旧元素靠后,所以旧元素中比新元素值大的就失去了作为备用最小值的意义,可以清除掉不管)同时,若队首元素超出范围则清除。求最大值的也类似。

e.g . 拿样例来说,元素存放在arr[]中,构造队列que{}。n为8,k为3.

插入扫过第一个元素,直接把下标放入得到que{0}。第二个元素3>1,放入下标后的到que{0,1}.第三个元素-1<1,将数值1和3的下标清除 ,放入下标后得到que{2},这是输出arr[2].第四个元素-3<-1,得到que{3},输出arr[3],第五个5>-3.得到que{3.4}....


#include <iostream>#include <cstdio>using namespace std;int arr[1000000];int que[1000000];int head = 0, rear = -1;int empty(){    return rear < head;}int main(){    int n, k;    scanf("%d%d", &n, &k);    for(int i = 0; i < n; i++) scanf("%d", &arr[i]);    int  i = 0;    //最小值    while(i < n){             while(arr[i] <= arr[que[rear]] && rear >= head) rear--; //找到新插入元素的位置,并清除大于该元素的旧元素        que[++rear] = i;  //插入新元素的下标                while(!empty() && que[head] < i-k+1) head++;  //清除队首超出范围的元素           if(i >= k-1) printf("%d ", arr[que[head]]);  //输出队首(最小值)        i++;    }    printf("\n");    i = head = 0;    rear = -1;    while(i < n){        while(arr[i] >= arr[que[rear]] && rear >= head) rear--;        que[++rear] = i;                while(!empty() && que[head] < i-k+1) head++;           if(i >= k-1) printf("%d ", arr[que[head]]);        i++;    }    return 0;}



二、

描述

农夫John 的N(1 ≤ N ≤ 80,000)只奶牛中,有一些也许正在经历发型糟糕的一天。每只奶牛对自己乱糟糟的发型都有自知之明,农夫John想知道所有奶牛能看到其他奶牛头顶的数量之和。

任意奶牛i身高记为 hi (1 ≤ hi ≤ 1,000,000,000),所有奶牛面向东方(本题示意图的右面)依次站成一条线。因此,奶牛i能够看到在它前面的(奶牛i+1,i+2…)所有身高比它低的奶牛,直到被一头比它高的奶牛挡住

考虑如下的例子:

        ==       ==   -   =         Cows facing right ->=   =   == - = = == = = = = =1 2 3 4 5 6 

奶牛#1 可以看见奶牛#2, 3, 4的头顶

奶牛#2 无法看到任何奶牛的头顶

奶牛#3可以看见奶牛#4的头顶

奶牛#4无法看到任何奶牛的头顶

奶牛#5可以看见奶牛#6的头顶

奶牛#6无法看到任何奶牛的头顶!

用ci表示奶牛i能够看到头顶的奶牛个数;请计算c1 至cN的和。对于上面这个例子,其和为:3 + 0 + 1 + 0 + 1 + 0 = 5。

输入
第1行:奶牛数N


第2行至N+1行:第i+1行包含一个整数,表示奶牛i的高度

输出
第1行:c1 至cN的累加和
样例输入
610374122
样例输出
5

  这道题目跟上一道类似,可以通过维护一个单调递减队列实现。构造一个单调递减队列存储高度,每次插入一个新元素时,清除比该元素小的元素(因为高度小被当前奶牛挡住了,不用再计算了),该元素作为队尾元素,此时排在该元素前面的都是比他高的都能看见他,所以ans += rear-head;

#include <iostream>using namespace std;int que[80000];int head = 0, rear = -1;int main(){    int n, input;    long long ans = 0;    cin >> n;    while(n--){        cin >> input;        while(rear >= head && input >= que[rear]) rear--; //清除矮的        que[++rear] = input; //插入        ans += rear - head;  //统计    }    cout << ans << endl;    return 0;}







0 0
原创粉丝点击