动态规划训练之十九
分析:
考虑初级的dpO(N3)
code:
for(ri K=1;K<=n;K++)
for(ri i=1;i<=n;i++){
for(ri j=i-1;j>=1;j--)
if(sum[i]-sum[j]<=w)
dp[i][K]=min(dp[j][K-1]+K*(sum[i]-sum[j])+querymax(j+1,i)-querymin(j+1,i),dp[i][K]);
考虑能不能降维
发现这个K很多事
想到把K这一维删去
这里就要用到费用提前算的思想了
补充:费用提前算
https://www.luogu.org/problem/P2365
分析:
考虑最原始的dp:
f[i,j]=min{f[k,j-1]+(s×j+sumT[i])×(sumF[i]-sumF[k])}(0<=k<i)
复杂度N3
把费用提前算:将以后会算上的,先加上,除掉后效性
f[i]=min
这题数据也就N2,其实这题还可以斜率优化,当然懒得说了
回到正题
dp[i,K]=min(dp[j,K-1]+K*(sum[i]-sum[j])+querymax(j+1,i)-querymin(j+1,i),dp[i,K])
经过变换:
dp[i]=min{dp[j]+(sum[i]-sum[j])+querymax(j+1,i)-querymin(j+1,i)+sum[n]-sum[i]};
后面的相比前面的系数差1
这样是N2的,还是不行怎么办
发现还可以单调队列优化
code by std:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int t,n,wi,q[maxn];
ll sum[maxn],stmn[maxn][30],stmx[maxn][30],f[maxn];
inline void stwk(){
for(int i=1;i<=t;i++) for(int j=1;j+(1<<(i-1))<=n;j++){
stmn[j][i]=min(stmn[j][i-1],stmn[j+(1<<(i-1))][i-1]);
stmx[j][i]=max(stmx[j][i-1],stmx[j+(1<<(i-1))][i-1]);
}
}
inline ll ask(int l,int r){
int k=log2(r-l+1);
ll minn=min(stmn[l][k],stmn[r-(1<<k)+1][k]),maxx=max(stmx[l][k],stmx[r-(1<<k)+1][k]);
return maxx-minn;
}
int main()
{
scanf("%d%d",&n,&wi);
t=log2(n)+1;
for(int x,i=1;i<=n;i++){
scanf("%d",&x);
sum[i]=(ll)sum[i-1]+x;stmn[i][0]=(ll)x;stmx[i][0]=(ll)x;
}
stwk();
memset(f,0x3f,sizeof(f));
f[0]=0;int l=0,r=0;
for(int i=1;i<=n;i++){
while(l<=r && sum[i]-sum[q[l]]>wi) l++;
for(int j=l;j<=r;j++) f[i]=min(f[i],f[q[j]]+sum[n]-sum[q[j]]+ask(q[j]+1,i));
while(l<=r && f[i]<=f[q[r]]) r--;
q[++r]=i;
}
printf("%lld\n",f[n]);
return 0;
}
开始我有疑问,为什么已经是单调队列优化dp了,中间还要枚举?
后来幡然醒悟,因为价值还有最大值-最小值啊
至于复杂度,应该不高吧