1. 程式人生 > >HDU 3415 Max Sum of Max-K-sub-sequence【單調隊列】

HDU 3415 Max Sum of Max-K-sub-sequence【單調隊列】

一段 大於 pro tdi 隊列 就是 int amp scanf

<題目鏈接>

題目大意:

給你一段從1~N的圓形序列,要你求出這段圓形序列中長度不超過K的最大連續子序列之和是多少,並且輸出這子序列的起點和終點。

解題分析:

既然是求連續子序列之和,我們不妨將這段序列的前綴和算出來。因為本題規定了序列的最長長度,很容易想到單調隊列,我們可以用一個單調隊列去維護前綴和的最小值,讓每一次移動的最小的前綴和都為單調隊列的隊首,也就是該單調隊列為單調遞增的序列。對於新訪問的點,如果它的前綴和小於隊列的尾端,那麽刪除隊列的尾端,將其插入隊列的合適位置,維護隊列的遞增性。如果遍歷到的點的下標與隊列頭節點下標之差>K,說明隊列維護的連續序列的長度不符合題目要求,將隊列頭結點刪除。最終,每一次遍歷,隊列的頭結點的下標到 i (i此時為隊列的尾節點下標) 為每一次的最大連續和的序列(因為隊列的頭結點始終維護的是在指定區間內最小的前綴和)。需要註意的是,假如 隊列記錄的是 a~ i 這段連續子序列,那麽只需要記錄 a-1 就夠了,因為[a,i]這段序的和為 sum[i]-sum[a-1],方便計算。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int M=2*100000+5;
#define INF 1e9
int a[M],q[M];

int main(){
    int _;scanf("%d",&_);
    while(_--){
        int n,k;
        scanf("%d %d",&n,&k);
        a[0]=0;
        for(int i=1;i<=n;i++){
            scanf(
"%d",&a[i]); a[i]+=a[i-1]; } for(int i=n+1;i<n+k;i++){ //將這個序列補到n+k-1,因為每個位置作為單調序列的頭節點的情況都要考慮 a[i]=a[n]+a[i-n]; } int mx=-INF,st,et; int head=0,tail=0; for(int i=1;i<=n+k-1;i++){ while(head<tail&&a[i-1
]<a[q[tail-1]])//tail-1才是尾節點的位置,tail為代插入節點的位置,該優先隊列維護的是前綴和的最小值,是一個關於前綴和的單調遞增序列,然後根據為尾節點-頭節點 --tail; q[tail++]=i-1; //q[]數組記下i-1的下標,方便求前綴和的時候做差 while(head<tail&&i-q[head]>k) //如果隊首坐標與隊尾坐標相差大於k,則將隊首刪除 ++head; if(mx<a[i]-a[q[head]]){ //如果單調隊列維護的這個區間和大於當前最大值 mx=a[i]-a[q[head]]; st=q[head]+1; et=i>n?i%n:i; } } printf("%d %d %d\n",mx,st,et); } return 0; }

2018-09-23

HDU 3415 Max Sum of Max-K-sub-sequence【單調隊列】