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