WA on 3 求助

P1886 滑动窗口 /【模板】单调队列

SICKO @ 2023-12-04 17:39:36

这是代码捏

#include<iostream>
#include<vector>
#include<algorithm>
#include<deque>
using namespace std;
const int N = 1e6+6;
deque<int> my_queue;
int num[N];
vector<int> min_ans;
vector<int> max_ans;
int main()
{
    int n, m;  cin>>n>>m;
    for(int i=1; i<=n; i++)
    {
        cin>>num[i];
    }
    if(m == 1)
    {
        for(int i=1; i<=n; i++) cout<<num[i]<<" ";
        cout<<"\n";
        for(int i=1; i<=n; i++) cout<<num[i]<<" ";
        return 0;
    }
//--------------------------------------------------------------------------------------------------------------------
    // 滑动窗口,处理最大值问题,则维护一个单调递减的队列
    // 预处理第一步,队列里面得有东西
    my_queue.push_back(1);
    // 预处理第二步,留下有可能成为最大值的值的地址
    for(int i=2; i<m; i++)
    {
        // 如果这个值比队尾的值要小或相等,那么队尾的值有可能成为之后区间的最大值,这个值也是,所以我们入队
        if(num[i] <= num[my_queue.back()])
        {
            my_queue.push_back(i);
        }
        // 如果这个值比队尾的值要大,那么队尾的值一定不可能成为之后区间的最大值了,我们就后端出列直到空或者符合小于等于
        else while(true)
        {
            my_queue.pop_back();
            if(my_queue.empty())
            {
                my_queue.push_back(i);
                break;
            }
            if(num[i] <= num[my_queue.back()])
            {
                my_queue.push_back(i);
                break;
            }
        }
    }
    // 主处理函数
    for(int i=m; i<=n; i++)
    {
        // 再重复一遍之前的处理
        // 如果这个值比队尾的值要小或相等,那么队尾的值有可能成为之后区间的最大值,这个值也是,所以我们入队
        if(num[i] <= num[my_queue.back()])
        {
            my_queue.push_back(i);
        }
        // 如果这个值比队尾的值要大,那么队尾的值一定不可能成为之后区间的最大值了,我们就后端出列直到空或者符合小于等于
        else while(true)
        {
            my_queue.pop_back();
            if(my_queue.empty())
            {
                my_queue.push_back(i);
                break;
            }
            if(num[i] <= num[my_queue.back()])
            {
                my_queue.push_back(i);
                break;
            }
        }
        // 最后别忘了判断队头是否合法。怎么判断,如果这个队头加上 m 的值小于等于当前位置,那么说明队头不在窗口内
        if(my_queue.front()+m<=i)  my_queue.pop_front();
        // 到了主处理函数处,我们就可以判断最大最小值了,按照之前的处理,最大值一定会出现在对头
        max_ans.push_back(num[my_queue.front()]);
    }
//---------------------------------------------------------------------------------------------------------------------
    // 接下来就是处理最小值问题了,最小值问题,我们就得维护单调递增的队列
    // 别忘了清空队列
    my_queue.clear();
    // 预处理第一步,队列里面得有东西
    my_queue.push_back(1);
    // 预处理第二步,留下有可能成为最小值的值
    for(int i=2; i<m; i++)
    {
        // 如果这个值比队尾的要大或者相等,那么说明这两值有可能成为之后的最小值,我们加到队列里面
        if(num[i] >= num[my_queue.back()])
        {
            my_queue.push_back(i);
        }
        else while(true)
        {
            // 否则队尾一定不可能成为之后区间的最小值,我们不断弹出队尾直到找到目标
            my_queue.pop_back();
            if(my_queue.empty())
            {
                my_queue.push_back(i);
                break;
            }
            if(num[i] >= num[my_queue.back()])
            {
                my_queue.push_back(i);
                break;
            }
        }
    }
    // 主处理
    for(int i=m; i<=n; i++)
    {
        // 照搬之前操作
        // 如果这个值比队尾的要大或者相等,那么说明这两值有可能成为之后的最小值,我们加到队列里面
        if(num[i] >= num[my_queue.back()])
        {
            my_queue.push_back(i);
        }
        else while(true)
        {
            // 否则队尾一定不可能成为之后区间的最小值,我们不断弹出队尾直到找到目标
            my_queue.pop_back();
            if(my_queue.empty())
            {
                my_queue.push_back(i);
                break;
            }
            if(num[i] >= num[my_queue.back()])
            {
                my_queue.push_back(i);
                break;
            }
        }
        // 别忘了判断队头是否合法。怎么判断,如果这个队头加上 m 的值小于等于当前位置,那么说明队头不在窗口内
        if(my_queue.front()+m<=i)  my_queue.pop_front();
        // 到了主处理函数处,我们就可以判断最大最小值了,按照之前的处理,最小值一定会出现在对头
        min_ans.push_back(num[my_queue.front()]);
    }
    // 输出答案
    for(int i:min_ans)
    {
        cout<<i<<" ";
    }
    cout<<"\n";
    for(int i:max_ans)
    {
        cout<<i<<" ";
    }
    return 0;
}

测试点 3 是 n=100m=10<a_i<=1000

很神奇的地方在于如果不加开头 if(m==1) 的判断会 WA on 3,但实际上去做其他题并不会出现这样子的情况

比如P2032 扫描,P2251 质量检测, 1084: 滑动窗口

求大佬帮帮


by SICKO @ 2023-12-04 17:44:42

实际上输出的结果也是一样的


by SICKO @ 2023-12-05 21:23:02

已解决,n==1 时并不是能一次性弹出所有队列中的不合法队头(数列第一个数会被插入两次)。

#include<iostream>
#include<vector>
#include<algorithm>
#include<deque>
using namespace std;
const int N = 1e6+6;
deque<int> my_queue;
int num[N];
vector<int> min_ans;
vector<int> max_ans;
int main()
{
    int n, m;  cin>>n>>m;
    for(int i=1; i<=n; i++)
    {
        cin>>num[i];
    }
    // if(m == 1)
    // {
    //     for(int i=1; i<=n; i++) cout<<num[i]<<" ";
    //     cout<<"\n";
    //     for(int i=1; i<=n; i++) cout<<num[i]<<" ";
    //     return 0;
    // }
//--------------------------------------------------------------------------------------------------------------------
    // 滑动窗口,处理最大值问题,则维护一个单调递减的队列
    // 预处理第一步,队列里面得有东西
    my_queue.push_back(1);
    // 预处理第二步,留下有可能成为最大值的值的地址
    for(int i=2; i<m; i++)
    {
        // 如果这个值比队尾的值要小或相等,那么队尾的值有可能成为之后区间的最大值,这个值也是,所以我们入队
        if(num[i] <= num[my_queue.back()])
        {
            my_queue.push_back(i);
        }
            // 如果这个值比队尾的值要大,那么队尾的值一定不可能成为之后区间的最大值了,我们就后端出列直到空或者符合小于等于
        else while(true)
            {
                my_queue.pop_back();
                if(my_queue.empty())
                {
                    my_queue.push_back(i);
                    break;
                }
                if(num[i] <= num[my_queue.back()])
                {
                    my_queue.push_back(i);
                    break;
                }
            }
    }
    // 主处理函数
    for(int i=m; i<=n; i++)
    {
        // 再重复一遍之前的处理
        // 如果这个值比队尾的值要小或相等,那么队尾的值有可能成为之后区间的最大值,这个值也是,所以我们入队
        if(num[i] <= num[my_queue.back()])
        {
            my_queue.push_back(i);
        }
            // 如果这个值比队尾的值要大,那么队尾的值一定不可能成为之后区间的最大值了,我们就后端出列直到空或者符合小于等于
        else while(true)
            {
                my_queue.pop_back();
                if(my_queue.empty())
                {
                    my_queue.push_back(i);
                    break;
                }
                if(num[i] <= num[my_queue.back()])
                {
                    my_queue.push_back(i);
                    break;
                }
            }
        // 最后别忘了判断队头是否合法。怎么判断,如果这个队头加上 m 的值小于等于当前位置,那么说明队头不在窗口内
        while(my_queue.front()+m<=i)  my_queue.pop_front();
        // 到了主处理函数处,我们就可以判断最大最小值了,按照之前的处理,最大值一定会出现在对头
        max_ans.push_back(num[my_queue.front()]);
    }
//---------------------------------------------------------------------------------------------------------------------
    // 接下来就是处理最小值问题了,最小值问题,我们就得维护单调递增的队列
    // 别忘了清空队列
    my_queue.clear();
    // 预处理第一步,队列里面得有东西
    my_queue.push_back(1);
    // 预处理第二步,留下有可能成为最小值的值
    for(int i=2; i<m; i++)
    {
        // 如果这个值比队尾的要大或者相等,那么说明这两值有可能成为之后的最小值,我们加到队列里面
        if(num[i] >= num[my_queue.back()])
        {
            my_queue.push_back(i);
        }
        else while(true)
            {
                // 否则队尾一定不可能成为之后区间的最小值,我们不断弹出队尾直到找到目标
                my_queue.pop_back();
                if(my_queue.empty())
                {
                    my_queue.push_back(i);
                    break;
                }
                if(num[i] >= num[my_queue.back()])
                {
                    my_queue.push_back(i);
                    break;
                }
            }
    }
    // 主处理
    for(int i=m; i<=n; i++)
    {
        // 照搬之前操作
        // 如果这个值比队尾的要大或者相等,那么说明这两值有可能成为之后的最小值,我们加到队列里面
        if(num[i] >= num[my_queue.back()])
        {
            my_queue.push_back(i);
        }
        else while(true)
            {
                // 否则队尾一定不可能成为之后区间的最小值,我们不断弹出队尾直到找到目标
                my_queue.pop_back();
                if(my_queue.empty())
                {
                    my_queue.push_back(i);
                    break;
                }
                if(num[i] >= num[my_queue.back()])
                {
                    my_queue.push_back(i);
                    break;
                }
            }
        // 别忘了判断队头是否合法。怎么判断,如果这个队头加上 m 的值小于等于当前位置,那么说明队头不在窗口内
        while(my_queue.front()+m<=i)  my_queue.pop_front();
        // 到了主处理函数处,我们就可以判断最大最小值了,按照之前的处理,最小值一定会出现在对头
        min_ans.push_back(num[my_queue.front()]);
    }
    // 输出答案
    for(int i:min_ans)
    {
        cout<<i<<" ";
    }
    cout<<"\n";
    for(int i:max_ans)
    {
        cout<<i<<" ";
    }
    return 0;
}

if 判断方法改为 while 即可。


|