P3195 [HNOI2008] 玩具装箱(斜率优化DP)

题目链接
\(d[i]\)为将前 \(i\) 个玩具装入箱中所需得最小费用
容易得到动态转移方程:
\[d[i] = min(d[j] + (s[i]-s[j]+i-j-1-L)^2), (j<i)\]
其中\(s[i] = \sum_1^iC[i]\),普通DP复杂度为\(O(n^2)\)。经过斜率优化后将变为\(O(n)\)
仔细观察我们便于表示可以令\(f[i] = s[i]+i\)
那么式子变成了

\[d[i] = min(d[j] + (f[i]-f[j]-1-L)^2)\]

我们讨论\(j_1,j_2(1\le j_1< j_2<i)\)决策,假设\(j_2\)要比\(j_1\)更优,那么有

\(d[j_1] + (f[i] -f[j_1]-1-L)^2 \ge d[j_2]+(f[i]-f[j_2]-1-L)^2\)

展开后得到

\(d[j_1] + f[i]^2 - 2\times f[i]\times (f[j_1]+1+L)+(f[j_1]+1+L)^2 \ge d[j_2]+f[i]^2-2\times f[i]\times (f[j_2]+1+L)+(f[j_2]+1+L)^2\)

移项后可得

\(2\cdot f[i]\ge {d[j_2]+(f[j_2]+1+L)^2-d[j_1]-(f[j_1]+1+L)^2 \over f[j_2]-f[j_1]}\)

\(g[i] = f[i]+1+L\), 则有

\(2\cdot f[i]\ge {(d[j_2]+g[j_2])-(d[j_1]+g[j_1])\over f[j_2]-f[j_1]}\)

所以用一个队列维护决策集,当\(j_1<j_2\),并且上式满足时,\(j_1\) 出队。
又由于\(f[i]\)\(i\)单调递增。所以计算\(d[i]\)之后要将 \(i\) 入队时,要及时排除掉不可能作为决策的元素。
如何计算?队尾的斜率也要满足单调性,保持跟\(f[i]\)的单调性一致即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 50010;
typedef long long ll;
typedef long double db;
db c[N],d[N],f[N],s[N],g[N];
int n,L;
int q[N],l,r;
db sqr(db x){return x * x;}
db slope(int i,int j){
    return ((d[i] +  g[i]) - (d[j] + g[j])) / (f[i] - f[j]);
}
int main(){
    scanf("%d%d",&n,&L);
    l=r=1;
    for(int i=1;i<=n;i++){
        cin>>c[i];
        s[i]=s[i-1] + c[i];
        f[i] = s[i] + i;
        g[i] = (f[i] + 1 + L) * (f[i] + 1 + L);
    }
    g[0] = (ll)(1+L)*(1+L);//注意0号元素的g值初始化
    for(int i=1;i<=n;i++){
        while(l < r && slope(q[l],q[l+1]) < 2 * f[i])l++;
        int j = q[l];
        d[i] = d[j] + sqr(f[i]-f[j]-1-L);
        while(l < r && slope(q[r],q[r-1]) > slope(i,q[r-1]))r--;//满足队尾斜率单调性
        q[++r] = i;//入队
    }
    printf("%lld\n",(ll)d[n]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/1625--H/p/11266927.html