单调队列的应用

一、

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

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


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


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

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的累加和 样例输入
6
10
3
7
4
12
2
样例输出
5

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

#include 

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;
}





 
  


你可能感兴趣的:(数据结构)