poj 3017 单调队列+dp+sbt

/*
题意:
   将长度为n的序列分成若干段,使每段之和不大于m,
且使各段中最大值之和最小,输出该值。
解法:
    由于n巨大,所以想到了滚动数组优化!
*/
#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int maxn=111111;
int q[maxn],a[maxn],n;
long long dp[maxn],sum,m;
int main()
{
    int i,j,l,r,flag,pre;
    // freopen("//media/学习/ACM/input.txt","r",stdin);
    while(scanf("%d%lld",&n,&m)!=EOF)
    {
        multiset<long long>s;
        s.clear();
        for(dp[n]=r=-1,pre=1,sum=l=flag=0,i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
            if(flag)continue;
            if(a[i]>m)flag=1;
            while(sum>m)sum-=a[pre++];
            while(l<=r&&a[i]>=a[q[r]])
            {
                if(l<r)s.erase(dp[q[r-1]]+a[q[r]]);
                r--;
            }
            q[++r]=i;
            if(l<r) s.insert(dp[q[r-1]]+a[q[r]]);
            while(q[l]<pre)
            {
                if(l<r)s.erase(dp[q[l]]+a[q[l+1]]);
                l++;
            }
            dp[i]=dp[pre-1]+a[q[l]];
            long long ans=*s.begin();
            if(l<r&&dp[i]>ans)dp[i]=ans;
        }
        if(flag)puts("-1");
        else
        printf("%lld\n",dp[n]);
    }
    return 0;
}

你可能感兴趣的:(poj 3017 单调队列+dp+sbt)