http://poj.org/problem?id=3017
poj 3017 Cut the Sequence
对我来说……挺不好理解的一道题…………
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <set>
using namespace std;
const int maxn= 100005;
const int INF= 0x3f3f3f3f;
long long dp[maxn], m;
int a[maxn], n, q[maxn];
multiset<long long> tree;
void dynamic(){
int i, j, k, h, r, lef= 1, flag= 1;
long long sum= 0;
h= 0, r= -1;
/*
状态转移方程:
dp[i]= dp[lef-1] + max(a[lef]...a[i])
用单调队列q维护区间最大点,元素值及下标都单调递减
sbt维护最优值,堆顶为最小值
*/
for( i= 1; i<= n; i++){
sum+= a[i];
while( sum > m) sum-= a[lef++]; //求最左可以分为一组的点
if( lef > i){
flag= 0;
break;
}
while( h <= r && a[q[r]] <= a[i] ){//单调队列中找到i的位置
/*
删除sbt中不符合的数据
若去q[r]删除掉,则对应的sbt中的dp[q[r-1]] + a[q[r]]项也应去掉
其中,a[q[r]]为[q[r], i-1]的最大值,但a[q[r]]同样也大于a[q[r-1]]
即dp[i-1]= dp[a[q[r-1]]] + max(a[q[r-1]]...a[q[r]]...a[i-1])为sbt中被删除的值
*/
if( h < r)
tree.erase(dp[q[r-1]] + a[q[r]]);
r--;
}
q[++r]= i; //i点存入队列
if( h < r) tree.insert( dp[q[r-1]] + a[q[r]]);//新更新的队列的队首值加入队列,但不一定是目前最优值
while( q[h] < lef){// 队首元素不在区间内时删除,维护单调队列取值在规定范围内
if( h < r)
tree.erase( dp[q[h]] + a[q[h+1]]);//sbt中删除不在范围内的点的值,a[q[h+1]]为(q[h], i]中的最大值
h++;
}
dp[i]= dp[lef-1] + a[q[h]]; //下限值特判
if( h < r && dp[i] > *(tree.begin())) dp[i]= *tree.begin();
cout<<i<<" "<<dp[i]<<" "<<dp[lef-1] + a[q[h]]<<" "<<(*tree.begin())<<endl;;
}
if( flag)printf("%I64d\n", dp[n]);
else printf("-1\n");
}
int main(){
//freopen("1.txt", "r", stdin);
int i, j, k;
while( scanf("%d%I64d", &n, &m) != EOF){
a[0]= dp[0]= 0;
for( i= 1; i<= n; i++){
scanf("%d", &a[i]);
}
dynamic();
}
return 0;
}