动态规划训练之十九

分析:

考虑初级的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了,中间还要枚举?

后来幡然醒悟,因为价值还有最大值-最小值啊

至于复杂度,应该不高吧

posted @ 2019-11-04 11:05  wzx_believer  阅读(160)  评论(0编辑  收藏  举报