切蛋糕(單調隊列)

【切蛋糕】
-題目描述-
今天是小Z的生日,同學們爲他帶來了一塊蛋糕。這塊蛋糕是一個長方體,被用不同色彩分成了N個相同的小塊,每小塊都有對應的幸運值。
小Z作爲壽星,自然希望吃到的第一塊蛋糕的幸運值總和最大,但小Z最多又只能吃M小塊(M≤N)的蛋糕。
吃東西自然就不想思考了,於是小Z把這個任務扔給了學OI的你,請你幫他從這N小塊中找出連續的k塊蛋糕(k≤M),使得其上的幸運值最大。洛谷
-輸入格式-
第一行是兩個整數N,M。分別代表共有N小塊蛋糕,小Z最多隻能吃M小塊。
第二行用空格隔開的N個整數,第i個整數Pi代表第i小塊蛋糕的幸運值。
-輸出格式-
輸出文件cake.out只有一行,一個整數,爲小Z能夠得到的最大幸運值。
-樣例數據-
input

5 2
1 2 3 4 5

----------
6 3
1 -2 3 -4 5 -6


output

9

----------
5

-分析-
暴力的思想:
三重循環,枚舉第一塊的位置i,枚舉最後一塊的位置j,再將區間中的數累加得到答案,進行更新。
在這上面我們繼續優化。可以預處理出前綴和,減少一重循環。
僞代碼:

for(int i=1;i<=塊數;i++)
{
  cin>>a[i];
  a[i]+=a[i-1];
 }//前綴和
 for(int i=1;i<=塊數;i++)
  for(int j=0;j<=m;j++)
   ans=max(ans,a[i+m]-a[i-1]);

正解:
用單調隊列進行維護。
我們想要得到幸運值最大。在幸運值一定的情況下(即a[i]固定的情況下),我們需要減去的幸運值(a[j])儘量小。但是現在多了m塊數的限定,單單這樣做很可能會超出m快數的限定。所以在此之上,我們不單單要將幸運值進行比較,還要將隊中的蛋糕與當前的i之間的蛋糕塊數大於m的通通彈出隊列。
-代碼-

#include<bits/stdc++.h>
using namespace std;
int n,m;
int p[555555]={};
int q[555555]={};
int head=1;
int tail=0;
int ans=0;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>p[i];
        p[i]+=p[i-1];
    }//前綴和處理
    q[++tail]=0;//首先將0放入隊列
    for(int i=1;i<=n;i++)
    {
         ans=max(ans,p[i]-p[q[head]]);
        if(p[q[head]]>=p[i])
        {
            tail=head;
            q[head]=i;
        }//入隊
        else
        for(int j=tail;j>=head;j--)
         if(p[q[j]]<p[i])
         {
            q[j+1]=i;
            tail=j+1;
            break;
         }//維護隊列的單調性(單調遞增)
        for(;q[head]<=i-m&&head<=tail;)
         head++;//判斷有沒有超出塊數的限制
    }
    cout<<ans<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章