[HNOI2008]玩具装箱TOY
题意
思考
写的第一道斜率优化…感觉这个板题还蛮简单的
首先按普通思路,我们令 \(dp[i]\),为前 \(i\) 个物品的最小价值,那么可以很容易列出方程:
\[dp[i] = min\{ dp[j]+(sum[i]-sum[j]+i-j-1-L)^2 \}
\]
这是一个 \(O(n^2)\)的 \(dp\),我们考虑如何优化它:
令 \(s[x] = sum[x] + x,\ L=L+1\),则
\[dp[i] = dp[j]+(sum[i]-sum[j]+i-j-1-L)^2 \\
= dp[j] + (s[i] - s[j] - L) ^ 2 \\
= dp[j] + s[i]^2 + (s[j]+L)^2 - 2*s[i]*(s[j]+L)\]
等式整理为:
\[dp[i] + 2 * s[i] * (s[j] + L) = dp[j] + s[i]^2 + (s[j] + L) ^ 2
\]
在这个式子里面,我们把 \(dp[j] + s[i]^2 + (s[j] + L)\) 看作因变量, \(s[j]+L\)看作自变量,\(dp[i]\) 看作 \(y\) 轴截距,\(2*s[i]\)(已知量)看作直线斜率,那么我们就是要找到一个决策点 \(j\) 使得截距最小,根据 \(k=2*s[i]\) 我们可以发现这个斜率是单调递增的,那么我们维护的是一个下凸包,不多赘述。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double D;
const int N = 50050;
ll f[N], s[N], sum[N], n, L;
ll q[N], h, t;
D X(ll i){ return (D)s[i] + (D)L; }
D Y(ll i){ return (D)f[i] + (D)X(i) * (D)X(i); }
D slope(ll i, ll j){ return ( Y(i) - Y(j) ) / ( X(i) - X(j) ); }
int main(){
cin >> n >> L; L ++;
for(ll i=1; i<=n; i++) cin >> sum[i], sum[i] += sum[i-1], s[i] = sum[i] + i;
h = 1, t = 1;
for(ll i=1; i<=n; i++){
while(h < t && slope(q[h], q[h+1]) <= 2 * s[i]) h ++;
f[i] = f[q[h]] + (s[i] - s[q[h]] - L) * (s[i] - s[q[h]] - L);
while(h < t && slope(q[t], q[t-1]) >= slope(q[t], i)) t --;
q[++t] = i;
}
cout << f[n];
return 0;
}
总结
斜率优化中某些无关量可以在计算斜率的时候消去,所以不用管。
注意当前不变量和变量之间的关系。
注意变量类型。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步