洛谷p1886(滑动窗口,单调队列)

这里简单来模拟一下:

1.开始front是0,rear是-1,两个while都不符合条件,执行rear++,此时,此时记下第一个元素的坐标0和值1

2.i++,变为1,第一个while不执行,因为第二个条件中的范围没有超出,再看第二个while,两个条件都符合,rear--,后++(此时rear变为0)。rear的下标变为1,值为3(想办法维护最大值在front的位置)
 3.i变为2,第一个while还是不执行,因为没有超出范围,第二个while也不执行,因为nums[i](此时是-1)小于maxQueue[rear].index(此时是3),然后rear++(变为1),rear的下标变为2,值变为-1,这时的i>=k-1(即已达到动画窗格的大小范围),输出front的值。此时的动画窗格中的元素排列是(3 -1),为什么1没有了呢,因为被我们弹出去了哈哈

4.以此类推...

#include <stdio.h>
#include <stdlib.h>

// Structure to represent a queue element
struct QueueElement {
    int index;
    int value;
};

// Function to print the minimum values in each sliding window
void printMinValues(const int nums[], int n, int k) {
    struct QueueElement* minQueue = (struct QueueElement*)malloc(n * sizeof(struct QueueElement));
    int front = 0, rear = -1;

    // Iterate through the array
    for (int i = 0; i < n; ++i) {
        // Remove elements that are out of the current window
        while (front <= rear && minQueue[front].index < i - k + 1) {
            front++;
        }

        // Remove elements that are smaller than the current element from the back of the queue
        while (front <= rear && nums[minQueue[rear].index] > nums[i]) {
            rear--;
        }

        // Add the current element to the queue
        rear++;
        minQueue[rear].index = i;
        minQueue[rear].value = nums[i];

        // Output the minimum value for each window
        if (i >= k - 1) {
            printf("%d ", minQueue[front].value);
        }
    }
    printf("\n");

    free(minQueue);  // Free the dynamically allocated memory
}

// Function to print the maximum values in each sliding window
void printMaxValues(const int nums[], int n, int k) {
    struct QueueElement* maxQueue = (struct QueueElement*)malloc(n * sizeof(struct QueueElement));
    int front = 0, rear = -1;

    
    for (int i = 0; i < n; ++i) {       
        while (front <= rear && maxQueue[front].index < i - k + 1) {
            front++;
        }
        //非空的队列,且front的坐标超过了k所包含的范围
   
        
        while (front <= rear && nums[maxQueue[rear].index] < nums[i]) {
            rear--;
        }
        
        //将当前元素的索引 i 入队,因为它可能成为后面窗口中的最大值。
        
        rear++;
        maxQueue[rear].index = i;
        maxQueue[rear].value = nums[i]; 

        // Output the maximum value for each window
        //窗口形成的条件就是
        if (i >= k - 1) {
            printf("%d ", maxQueue[front].value);
        }
    }
    printf("\n");

    free(maxQueue);  // Free the dynamically allocated memory
}

int main() {
    int n, k;
    scanf("%d %d", &n, &k);

    int* nums = (int*)malloc(n * sizeof(int));
    //数组给的比较大,所以用动态内存分配
    for (int i = 0; i < n; ++i) {
        scanf("%d", &nums[i]);
    }

    printMinValues(nums, n, k);
    printMaxValues(nums, n, k);

    free(nums);  // Free the dynamically allocated memory

    return 0;
}

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: p1886 滑动窗口滑动窗口是一种常用的算法,用于解决一些区间相关的问题。它的基本思想是维护一个大小固定的窗口窗口每次向右移动一个单位,同时更新窗口内的信息。这个算法的时间复杂度通常是 O(n)。 【模板】单调队列单调队列是一种特殊的队列,它的队首和队尾都是单调的。在使用单调队列时,我们通常需要维护一个单调递增或单调递减的队列单调队列可以用来解决一些区间最值相关的问题,例如滑动窗口的最大值或最小值等。单调队列的时间复杂度通常是 O(n)。 ### 回答2: P1886滑动窗口是一道比较经典的题目,也是单调队列的一个很好的练习题目。这道题目大致分为两个步骤:首先,我们需要用一个循环来遍历整个数组;其次,我们需要用一个双端队列(即单调队列)来储存当前窗口内的元素,并维护队列中的单调性,最后将最大值输出。 我们可以将数组想象成一个长度为 $n$ 的窗口,其中每个元素代表着这个窗口内的一个数,然后随着窗口滑动,这个窗口内的元素也会不断地变换。因此,我们需要用循环来遍历整个数组,每次将窗口向右移动一个单位。 对于每个窗口,我们需要用一个双端队列(即单调队列)来维护队列中的单调性,并找出最大值。这里的单调性指队列中的元素呈单调递减或单调递增的关系。为了实现这个目标,我们需要在处理每个元素时,将其与队列末尾的元素进行比较: 如果当前元素 $a_i$ 大于队列末尾的元素,则说明队列末尾的元素不可能成为最大值,因此我们可以将其从队列中删除; 如果当前元素 $a_i$ 小于队列末尾的元素,则将其加入队列的末尾; 如果当前元素 $a_i$ 等于队列末尾的元素,则我们不需要将其加入队列,因为它的位置可以被后面的元素取代。 在以上处理完之后,队列的头部元素即为目前窗口内的最大值,每次我们将其输出即可。由于每个元素最多只有进队和出队各一次,因此总时间复杂度为 $O(n)$。 在实现这个算法时,需要注意队列存储的是下标而不是元素,这样才能判断队列末尾的元素是否在窗口内。同时,队列中存储的是一个递减的序列,对于相等的元素就不需要再次加入队列了。 ### 回答3: p1886 滑动窗口 /【模板】单调队列是一道经典的算法问题,其通过建立单调队列来实现滑动窗口的计算。 滑动窗口是一种非常常见的算法,它主要是在一个序列中滑动一个长度固定的窗口,以便对窗口内的元素进行计算。在这个过程中,每次将窗口向右移动一步,依次计算窗口内的元素。 滑动窗口的应用非常广泛,比如在在字符串匹配、时间序列分析、区间动态规划等领域都能见到其身影。 而通过单调队列来实现滑动窗口计算的方法主要是通过维护一个单调递减的队列来实现,在滑动窗口向右移动时,如果有新的元素进入窗口,就将队列末端所有比新元素大的元素弹出。这样保证队列存储的都是当前窗口内的最小值。 具体代码实现可以参考如下: ```c++ #include <iostream> #include <deque> using namespace std; int a[100005]; int n, k; void MonotonicQueue() { deque<int> q; for (int i = 0; i < n; i++) { //保证队列末端存的是当前窗口内的最小值 while (!q.empty() && q.front() <= i - k) q.pop_front(); while (!q.empty() && a[i] < a[q.back()]) q.pop_back(); q.push_back(i); if (i >= k - 1) cout << a[q.front()] << ' '; } } int main() { cin >> n >> k; for (int i = 0; i < n; i++) cin >> a[i]; MonotonicQueue(); cout << endl; return 0; } ``` 在这段代码里,我们先建立一个空的单调队列,并在for循环里遍历整个序列,每次将新元素加入队列时,先将末端所有比新元素大的元素弹出,以保证队列末端存储当前窗口内的最小元素,然后再将新元素加入到队列末端。当窗口长度达到k时,输出队列开头的元素,即为当前窗口内的最小值。 总体来说,p1886 滑动窗口 /【模板】单调队列是一道比较好理解的算法问题,大家可以多练习一下,掌握滑动窗口算法的精髓,以及单调队列的应用方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值