caioj 1172: 單調佇列(過渡題)
阿新 • • 發佈:2018-11-21
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;
}