poj 3017 dp+单调队列优化

来源:互联网 发布:mac pro安装windows10 编辑:程序博客网 时间:2024/06/07 00:54
/*题意:给你一个长度为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;}