BZOJ 1010 斜率优化dp
1010: [HNOI2008]玩具装箱toy
题意:n个玩具,每个长度ci,要把他们都放到容器中。如果同一容器中放多个玩具,这些玩具必须编号连续,且相邻两个玩具之间要加一个长度为1的填充物。如一个容器长度为x,其制作费用为(x-L)^2,(L是给出的常数),问总费用最少多少。
tags:还是没搞懂,看神犇代码强行敲的。。
大概理解:斜率优化是针对有f[i]=f[j]*x[i],不能用单调队列优化的情况,可以使复杂度从O(n^2)降到O(n)。类似凸包的Graham,利用斜率维护一个单调队列。但单调性和怎么求斜率搞不懂==
这题很容易想到dp[i]=min(dp[j]+(sum[i]-sum[j]+i-j-1-L)^2),但1e5必须要优化。最后不知道怎么就变到了斜率方程 (dp[k]+(f[k]+c)^2-dp[j]-(f[j]+c)^2)/2*(f[k]-f[j])<=f[i],然后f[i]递增,用单调队列维护一个下凸壳。如斜率(q[r],i)<斜率(q[r-1],q[r])时,队尾就是无效的,将其弹出。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define FF(i,a,b) for (int i=a;i<=b;i++) #define F(i,b,a) for (int i=b;i>=a;i--) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f typedef long long ll; const int N = 5e4+10; int n, L, C, c[N], q[N]; ll s[N], f[N]; double slo(int j, int k) { return (f[k]-f[j]+(s[k]+C)*(s[k]+C)-(s[j]+C)*(s[j]+C))/(2.0*(s[k]-s[j])); } void DP() { int l=1, r=0; q[++r]=0; FF(i,1,n) { while(l<r && slo(q[l],q[l+1])<s[i]) l++; int t=q[l]; f[i]=f[t]+(s[i]-s[t]-C)*(s[i]-s[t]-C); while(l<r && slo(q[r],i)<slo(q[r-1],q[r])) r--; q[++r]=i; } } int main() { scanf("%d %d", &n, &L); C=L+1; FF(i,1,n) scanf("%d", &c[i]), s[i]=s[i-1]+c[i]; FF(i,1,n) s[i]+=i; DP(); printf("%lld\n", f[n]); return 0; }