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;}