【题解】裁剪序列
【题解】【单调队列优化dp】裁剪序列
题目传送门
分析
首先考虑朴素的做法,设\(f_i\)表示把前i个数分成若干段,在满足每段中所有的数的和不超过M的前提下,各段的最大值之和最小是多少。容易写出状态转移方程:
但这样转移是\(O(N^2)\)的,我们再思考有哪些决策j是我们“真正需要”的(可以成为最优决策)
观察我们的状态转移方程,发现决策的两个部分:
1.\(f_j\)是关于j的单调不下降函数
2.\(\max\limits_{j+1 \leq k\leq i} \{a_k\}\)是关于j的单调不上升函数
所以,如果\(\max\limits_{j+1 \leq k\leq i} \{a_k\}=\max\limits_{j \leq k\leq i} \{a_k\}\)并且\(\sum_{p=j}^i a_p<=M\)
决策j-1一定比决策j更优
那假设我们记录一个maxn表示j到i的最大值,我们从大到小枚举j,每次更新maxn,根据上面的结论,只有使maxn变大的j才有可能作为最优决策。特殊地,若j为最小的满足\(\sum_{p=j+1}^i a_p<=M\)的j,其也有可能更新答案
如下图,j=3使得maxn变大,因此j=3可以作为最优决策。j=1是最小的满足\(\sum_{p=j+1}^i a_p<=M\)的j,所以j=1也可以作为最优决策

于是我们思考如何维护这个决策集合,即i增加到i+1时,决策集合发生了哪些变化。假设原先的决策集合为\(S=\{j_1,j_2,\cdots,j_s\}\),不妨令\(j_1<j_2<\cdots<j_s\),显然\(a[j_1]>a[j_2]>\cdots>a[j_s]\)
那么当i变化后,我们模拟一下S的更新,先将\(i-1\)与\(j_s\)进行比较,若\(a[i-1]>=a[j_s]\),把\(j_s\)扔出S,
然后,按照相同方式依次比较\(i-1\)与\(j_{s-1},j_{s-2},j_{s-3}\cdots,\)直到集合为空或\(a[i-1]<a[j_{s-x}]\),然后将i-1加入集合
除此之外我们还要从\(j_1\)开始逐个检验\(\sum_{p=j_x+1}^i a_p\)是否小于等于\(M\),若否,则将其出队
是的,我已经情不自禁开始说出队了,因为这实际上就是一个单调队列呀
但是题目到这还没有做完,因为单调队列存的每个元素都是可能的最优决策,若不加以优化,仍要遍历整个队列导致复杂度爆炸
我们再来回顾下,我们原始的方程式:
这个\(\max\limits_{j+1 \leq k\leq i} \{a_k\}\)好像很难处理,我们先想个简单的,如果min里面只有\(f_j\)的话怎么做?
既然是求最值,可以用一个二叉堆来维护,让它和单调队列建立一个映射,两个数据结构同时插入同时删除,至于处理\(\max\limits_{j+1 \leq k\leq i} \{a_k\}\),就轮到单调队列发挥作用了
在单调队列中,我要从尾部插入一个j,那我删除的那些节点的max就不用管了,而除了队尾,队列中的其他元素的max值也不会变(想一想,为什么),因此我只需在堆中更新队尾的max值
于是这道题就做完了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define int long long
inline int read()
{
register int x=0,w=1;
register char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-'){ch=getchar();w=-1;}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
struct node{
int id,val,y;
};//用结构体方便“懒惰删除”
bool operator<(const node &x,const node &y){
return x.val>y.val;
}
int q1[100005],l=1,r;
priority_queue<node>q2;
int n,m,st[100005][20],c[100005],a[100005],sum[100005];
int f[100005],used[100005],maxlog[100005];
int maxn(int l,int r)
{
int p=maxlog[r-l+1];
return max(st[l][p],st[r-(1<<p)+1][p]);
}
signed main()
{
n=read();m=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
if(a[i]>m){
cout<<-1<<endl;
return 0;
}
sum[i]=sum[i-1]+a[i];
c[i]=lower_bound(sum,sum+i+1,sum[i]-m)-sum+1;
// if(c[i]==1) c[i]=0;
st[i][0]=a[i];
}
for(int len=1;len<=n;++len)
{
maxlog[len]=maxlog[len-1];
if(1<<(maxlog[len]+1)<=len) maxlog[len]++;
}
for(int len=1;len<=maxlog[n];++len)
{
for(int i=1;i+(1<<len)-1<=n;++i)
{
st[i][len]=max(st[i][len-1],st[i+(1<<(len-1))][len-1]);
}
}
// for(int i=1;i<=n;++i)
// cout<<sum[i]<<" ";
//puts("");
// for(int i=1;i<=n;++i)
// cout<<sum[i]-m<<" ";
//puts("");
// for(int i=1;i<=n;++i)
// cout<<c[i]<<" ";
memset(f,0x3f,sizeof f);
f[0]=0;
for(int i=1;i<=n;++i)
{
f[i]=min(f[i],f[c[i]-1]+maxn(c[i],i));
while(l<=r&&c[i]>q1[l]) l++,used[q1[l-1]]=1;
while(l<=r&&a[q1[r]]<=a[i]) r--,used[q1[r+1]]=1;
q1[++r]=i;
if(l<r)
q2.push((node){q1[r-1],f[q1[r-1]]+a[i],a[i]});
while(!q2.empty()&&(used[q2.top().id]||(q2.top().y<maxn(q2.top().id+1,i))))
q2.pop();
if(!q2.empty())
f[i]=min(f[i],q2.top().val);
}
// for(int i=1;i<=n;++i)
// cout<<f[i]<<" ";
//
cout<<f[n];
return 0;
}

浙公网安备 33010602011771号