题意
给定一个长度为 n n n的序列,可以分成和不超过 m m m的若干组,每组的权值是其中的最大值,求最小化的每组权值之和。
期望时间复杂度 : O ( n log n ) O(n\log n) O(nlogn)
solution
显然有一个朴素dp:设 f i f_i fi 为到第 i i i位的最小权值和, 有转移
f i = min 1 ≤ j < i ( f j + max j < k ≤ i ( a k ) ) ( ∑ k = j + 1 i a k ≤ m ) {\color{brown} f_i=\min_{1\le j< i} (f_j+\max_{j<k\le i}(a_k))}(\sum_{k=j+1}^{i} a_k\le m) fi=min1≤j<i(fj+maxj<k≤i(ak))(∑k=j+1iak≤m)
for(int i=1;i<=n;i++)
{
int tp=0,sum=0;
for(int j=i;j>=1;j--)
{
sum+=a[j];
if(sum>m) break;
tp=max(tp,a[j]),f[i]=min(f[i],f[j-1]+tp);
}
}
O ( n 2 ) O(n^2) O(n2)
优化——《算法进阶指南》
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int n,a[maxn],c[maxn],q[maxn];
int m,f[maxn];
multiset<int> s;
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
int sum=0;
for(int i=1,j=0;i<=n;i++)
{
sum+=a[i];
while(sum>m) sum-=a[j+1],j++;
c[i]=j;
}
int l=0,r=0;
for(int i=1;i<=n;i++)
{
while(l<=r&&q[l]<c[i]) s.erase(f[q[l]]+a[q[l+1]]),l++;
while(l<=r&&a[q[r]]<=a[i]) s.erase(f[q[r-1]]+a[q[r]]),r--;
if(l<=r) s.insert(f[q[r]]+a[i]);
q[++r]=i;
f[i]=f[c[i]]+a[q[l]];
if(!s.empty()) f[i]=min(f[i],*s.begin());
}
cout<<f[n]<<endl;
return 0;
}