【bzoj1010】玩具装箱toy——斜率优化dp
第一道自己推的斜率优化dp><
首先要明确一点:装进同一个容器的toys一定要是连着的几个(否则的话可以直接贪心)-->之前理解错题意WA了一次......
用sum[i]表示前缀和,f[i]表示前i个装进容器的最小费用,
容易知道
f[i]=max(f[i],f[j]+(sum[i]-sum[j]+i-(j+1)-l)2)(j<i)
令k<j<i,若要使f[j]>f[k],则需要满足:
f[j]+(sum[i]-sum[j]+i-(j+1)-l)2<f[k]+(sum[i]-sum[k]+i-(k+1)-l)2
令t[i]=sum[i]+i,则移项,可得:
f[j]-f[k]+(t[j]-t[k])*(t[j]+t[k])<2(t[i]-l-1)(t[j]-t[k])
即:
(f[j]-f[k]+t[j]2-t[k]2)/(t[j]-t[k])<2*(t[i]-l-1)
令g[i]=f[i]+t[i]2,则有
(g[j]-g[k])/(t[j]-t[k])<2*(t[i]-l-1)。
用s(i,j)表示i到j连线的斜率,
对于一个单调队列,每次对于队首,若s(q[h],q[h+1])<2*(ti-l-1),则此时的q[h]一定不会是现在和以后的最优解,h++;
对于队尾,若s(q[t-2],q[t-1])>s(q[t-1],i),如果s(q[t-2],q[t-1])<2*(t[i]-l-1),则s(q[t-1],i)<2*(t[i]-l-1),由i>q[t-1]可知此时i比q[t-1]要优;如果s(q[t-2],q[t-1])>2*(t[i]-l-1),则q[t-2]要比q[t-1]优。所以无论如何q[t-1]都不会是最优解,此时t--。
接着就是斜率优化dp的套路了:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 typedef long long LL; 5 const int maxn=5e4+5; 6 using namespace std; 7 LL a[maxn],q[maxn],sum[maxn],g[maxn],t[maxn],f[maxn]; 8 inline LL read() 9 { 10 LL anss=0,f=1;char c=getchar(); 11 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 12 while(c>='0'&&c<='9'){anss=anss*10+c-48;c=getchar();} 13 return anss*f; 14 } 15 inline double cal(LL a,LL b) 16 { 17 return 1.0*(g[a]-g[b])/(t[a]-t[b]); 18 } 19 int main() 20 { 21 int n,l; 22 sum[0]=0; 23 scanf("%d %d",&n,&l); 24 for(int i=1;i<=n;i++){ 25 a[i]=read(); 26 sum[i]=sum[i-1]+a[i]; 27 t[i]=sum[i]+i; 28 } 29 int h=0,ta=1;q[0]=0; 30 for(int i=1;i<=n;i++) 31 { 32 while(h<ta-1&&cal(q[h],q[h+1])<2*(t[i]-l-1))h++; 33 f[i]=f[q[h]]+(t[i]-t[q[h]]-l-1)*(t[i]-t[q[h]]-l-1); 34 g[i]=f[i]+t[i]*t[i]; 35 while(h<ta-1&&cal(q[ta-1],q[ta-2])>cal(q[ta-1],i))ta--; 36 q[ta++]=i; 37 } 38 printf("%lld",f[n]); 39 return 0; 40 }