poj 3017 dp+单调队列优化

/*
题意:给你一个长度为n的数列,要求把这个数列划分为任意块,每块的元素和小于m,使得所有块的最大值的和最小
分析:这题很快就能想到一个DP方程 f[ i ]=min{ f[ j ] +max{ a[ k ] }}( b[ i ]<j<i,j<k<=i)     b[ i ]到 i的和大于m
这个方程的复杂度是O(n^2),明显要超时的(怎么discuss都说数据弱呢= =)
然后是优化了,首先当然是要优化一个最大值的队列,使得这个队列的队首元素的到当前位置的和不超过m,
这样一个可行解就是,f[ i ]=f[b[ i ]-1]+a[ q[ l ]](即队首元素的值),
这并不是最优解,所以还要找到队列中的最优解,一个可能的最优解只能是这样的
 f[ q[ j ] ]+ a[ q[j +1 ]],也就是 a[ j ] 要大于后面的数,
 很显然,如果a[ j ]小于后面的数,那么我们就可以将 a[ j ] 划分到后面去,而取得更优解
 这里涉及的这个找最优解问题,
 因为一个m 定义成int WA了无数次。。。。伤不起!!!
*/
#include<iostream>
#include<algorithm>
#include<set>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=110002;
int q[maxn],a[maxn],pre,n;
__int64 sum,Max[maxn],m;
int main()
{
    scanf("%d%I64d",&n,&m);
    int i,j,l=0,r=-1,flag=0;
    pre=1,sum=0;
    Max[n]=-1;
    multiset<int>s;
    s.clear();
    for(i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        sum+=a[i];
        while(sum>m) sum-=a[pre++];
        if(a[i]>m) flag=1;
        if(flag) continue;
        while(l<=r&&a[i]>=a[q[r]])
        {
            if(l<r)s.erase(Max[q[r-1]]+a[q[r]]);
            r--;
        }
        q[++r]=i;
        if(l<r) s.insert(Max[q[r-1]]+a[q[r]]);
        while(q[l]<pre)
        {
            if(l<r)s.erase(Max[q[l]]+a[q[l+1]]);
            l++;
        }
        Max[i]=Max[pre-1]+a[q[l]];
        __int64 ans=*s.begin();
        if(l<r&&Max[i]>ans) Max[i]=ans;
    }
    if(flag) printf("-1\n");
    else
    {
        printf("%I64d\n",Max[n]);
    }
    return 0;
}

你可能感兴趣的:(优化)