caioj 1172: 单调队列(过渡题)

版权声明:有女朋友的老江的博客,转载请告知老江 https://blog.csdn.net/qq_42367531/article/details/82822194

1172: [视频]单调队列(过渡题)题目
【题意】
给定一个n个数的数列,从左至右输出每个长度为m的数列段内的最大数。
比如8个数的数列[1 3 -1 -3 5 3 6 7],m=3,那么每连续3个最大值如下:

位置

最大值

[1  3  -1] -3  5  3  6  7 

3

 1 [3  -1  -3] 5  3  6  7 

3

 1  3 [-1  -3  5] 3  6  7 

5

 1  3  -1 [-3  5  3] 6  7 

5

 1  3  -1  -3 [5  3  6] 7 

6

 1  3  -1  -3  5 [3  6  7]

7

【输入格式】
第一行两个整数n和m( 1<= n <= 20 0000,m<=n)。
下来给出n个整数。
【输出格式】
一行一个整数,表示每连续m个数的最大值。
【样例输入】
8 3
1 3 -1 -3 5 3 6 7
【样例输出】
3
3
5
5
6
7

思路:这道题绝对是单调队列里面最简单的一道题,只是过渡题,还不算是难的,单调队列和栈有点像,然后我也不说太多了因为这个在代码中写的超级详细了

/*单调队列和栈有点像,但是栈是先进后出;单调队列是先进先出
同时,单调队列是成递减的队列的,就是第一个也就是队头是最大的
队尾是最小的,然后无数次删减之后的队列就是要输出的答案 
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    int x,p;//x表示的是这个数的值,p表示的是在单调队列中的位置 
}list[210000];//记录当前这个数及前面的值和位置 
int a[210000];//储存当前这个数 
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    list[1].x=-999999999; list[1].p=0;//初始化,因为要求最大值所以要定义最小的值,第一个的位置为0 
    int head,tail;//队头和队尾 
    head=tail=1;//都要为1,不然就没有人 
    for(int i=1;i<=n;i++)//寻找最大值 
    {
        //一开始一定要 head<=tail 才表示这一条队列中有人 
        while(head<=tail && i-list[head].p>=m) head++;
        /*如果包括自己的前10个人不删掉,其他都要删掉,就是从第一个开始删,离自己最远的开始删
        也就是说 如果我是 15, 我要求的是 长度为5的队列, 那么包括我自己  15 14 13 12 11,这几个人不该删,因为这个才是完整的队列
        而10 也就是 15-m=15-5=10,这个也要删掉因为包括了自己,所以要 >=m, 除了 15 14 13 12 11不用删,其他全部删掉
        而 不是 head-- 而是 head++ 是因为 我们 head++就是增加后面的 tail, 如果是 head--,就要将整个队伍前移,自然没那么方便
        所以我们要用head--,
        总结起来就是 删队头,除了不该删的 
        */
        while(head<=tail && list[tail].x<=a[i]) tail--;
        /*
        这一步就是删队尾,更加复杂,head[tail].x也可以表示为自己,因为是从自己开始往前删减,所以才能是队头最大,队尾最小
        然后我们从自己的前面开始删,删啊删啊,比自己小的都要删掉,遇到比自己大的就停止,不进行
        比如说 总共有9个数 我是第6个  需要一个长度为 3 的队列, 那么 假设 10 20 15 7 9 10 33 24 17,我是第6个,
        然后我们经过上一步 就把前面的10 20 15 删掉了,但是他们还在因为我们并没有删,只是增加了结尾(上面提到)
        那么这一步就是从自己开始向前寻找,好的 找到了9,比自己小,删掉,这一回是真的删掉,因为这个数到后面就再也没有用了
        继续向前看到了7,也比自己小,继续删掉,那么在 7 9 10中 10为最大值,
        这个时候 15就不开心了,因为 删完 7 9之后, 15 在 10的前面,按常理讲,应该是要成为 和10在一起的最大值
        但是要记住,前面删掉了,如果没有删,15是根本用不到的,所以就是要有前面的那一步才能继续,然后 不能满足 15
        所以在  7 9 10中的最大值就是 10, 15自然有人处理
        然后处理之后剩下的就是  10 20 15 10 33 24 17,然后一直到所有m队列删完之后的队列就是最终的队列 
        */
        tail++; list[tail].x=a[i]; list[tail].p=i;
        /*
        因为前面是计算我自己前面的,所以tail++之后的tail,也就是现在的最后一个就是我自己的值
        然后这个最后一个尾巴的位置就是当前循环到的i,也就是我自己的位置 
        */
        if(i>=m) printf("%d\n",list[head].x);//找到的数大于等于要求的数之后,就可以直接输出最终的队列,
        //因为这个队列都是最大的队列,也就是所有队列的队头 
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42367531/article/details/82822194