有一个长度为n的序列({a_i}),现在求将其划分成若干个区间,并保证每个区间的和不超过m的情况下,每个区间的最大值的和的最小值,(0 < N ≤ 100 000)。
解
不难想到,设(f_i)表示把前i个位置划分后的所求,设s为a的前缀和,于是有
关键在于这是(O(n^2))递推,于是有以下结论优化
- f具有单调性
把得到(f_i)的方案中,去除掉第i个位置变为(f_{i-1}),要么不变,要么变小,而此时得到(f_{i-1}leq f_i),于是得证。
- 最优决策点j必然满足其中之一
- (max_{k=j}^i{a_k}=a_j)(即(a_j)大于等于(a_{j+1},...,a_i))
- (s_i-s_{j-1}>m)(即j是最小的j使(s_i-s_jleq m))
对于最优决策点而言,如果前面的决策点比其更优,必然有(s_i-s_{j-1}leq m),并且(f_{j-1}+max_{k=j}^i{a_k}leq f_j+max_{k=j+1}^i{a_k}),而(f)本身具有单调性,为了让结果满足条件(max_{k=j}^i{a_k}= max_{k=j+1}^i{a_k}),即(a_kleq max_{k=j+1}^i{a_k})。
于是当(s_i-s_{j-1}> m),(a_kgeq max_{k=j+1}^i{a_k})时,j可能为最优决策点。
因此对于即j是最小的j使(s_i-s_jleq m),我们假设在第i-1个位置的j已经维护出来,而考虑第i个位置,此时多加了一个(a_i),如果不能满足条件,只要往后拉即可,这样可以做到(O(n))。
至于(a_j)大于等于(a_{j+1},...,a_i),故如果转移到求(f_{i+1}),多了一个(a_{i+1}),如果(a_{i+1}>a_j),必然它就不再满足了,于是可以去除,而(s_{i}-s_{j})又是一个单调的函数,于是我们可以维护单调递减的单调队列维护决策点a的单调性,并保证s的不超过范围。
现在问题在于如何快速求出决策点转移过来的值,易知对于决策点j(f_j)已是确定了,因为单调队列的性质,容易知道,决策点j的(max_{k=j+1}^i{a_k})也就是j在单调队列中的位置的下一个位置的a,根据这一性质,注意对单调队列末尾的特判即可。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#define il inline
#define ri register
#define ll long long
#define Size 100100
using namespace std;
multiset<int>S;
ll dp[Size];
int a[Size],T[Size],L,R;
template<class free>
il void read(free&);
template<class free>
il free Min(free,free);
int main(){
int n;ll m,sum(0);read(n),read(m);
for(int i(1);i<=n;++i)
read(a[i]),dp[i]=1e11;dp[0]=0,L=1,R=0;
for(int i(1),p(0);i<=n;++i){sum+=a[i];
while(sum>m)sum-=a[p+1],++p;if(p==i)return puts("-1"),0;T[R+1]=i;
while(L<=R&&T[L]<p)S.erase(S.find(dp[T[L]]+a[T[L+1]])),++L;
while(L<=R&&a[T[R]]<a[i])S.erase(S.find(dp[T[R]]+a[T[R+1]])),--R;
if(L<=R)S.erase(S.find(dp[T[R]]+a[T[R+1]])),S.insert(dp[T[R]]+a[i]),
dp[i]=*S.begin();T[++R]=i;
if(dp[p]+a[T[L]]<dp[i])dp[i]=dp[p]+a[T[L]];S.insert(dp[i]+a[i+1]);
}printf("%lld",dp[n]);
return 0;
}
template<class free>
il free Min(free a,free b){
return a<b?a:b;
}
template<class free>
il void read(free &x){
x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}