原来的DP式子:
设f[i]为前i个玩具装箱的总费用
f[i] = min{ f[j]+ (i-(j+1)+s[i]-s[j]-l)^2 }
展开得
令g[i]=i+s[i]
h[j]=j+1+s[j]+l
f[i] = min{ f[j]+(g[i]-h[j]) ^2} = min{f[j] + h[j] ^ 2 - 2 * g[i] *h[j] } + g[i] ^ 2
设j1<j2
如果决策j2 比j1优
f[j1]+ h[j1]^ 2 - 2* g[i] *h[j1]>f[j2]+ h[j2]^ 2 - 2* g[i] *h[j2]
(h[j2]-h[j1])*2*g[i]>f[j2]+h[j2]^2-f[j1]-h[j1]^2
slope(j1,j2)<2*g[i]
g[i]单调递增
f[j]+h[j]^2单调递增
同时,如果在a,b,c中b最优,那么slope(a,b)<2*g[i] and slope(b,c)>2*g[i] ==>slope(b,c)>slope(a,b)斜率单调递增
用单调队列维护下凸壳(斜率是正的,即上下增减性相同)
平方要注意long long
还有 slope(j1,j2)虽然数值上等于slope(j2,j1) 但是放在不等式中如果改变j1, j2位置,实际上不等号是要改变的,所以slope是唯一确定的
#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> using namespace std; #define MAXN 50010 int n,l,top,tail,j; int q[MAXN],a[MAXN]; long long s[MAXN],f[MAXN]; long long F(int x) { return f[x]+s[x]*s[x]+2*l*s[x]; } double slope(int a,int b) { return (F(a)-F(b))/(s[a]-s[b]); } int main() { scanf("%d%d", &n, &l); l++; for (int i=1;i<=n;i++) scanf("%d", &a[i]); for (int i=1;i<=n;i++) s[i]=s[i-1]+a[i]; for (int i=1;i<=n;i++) s[i]+=i; top=1;tail=1; q[top]=0; for (int i=1;i<=n;i++) { while (top<tail && slope(q[top],q[top+1])<=2*s[i]) top++; j=q[top]; f[i]=f[j]+(s[i]-s[j]-l)*(s[i]-s[j]-l); while (tail>top && slope(q[tail],i)<=slope(q[tail-1],q[tail])) tail--; q[++tail]=i; } printf("%lld", f[n]); }