题意:给你一串长度不一定相同的物体,问你怎么分段使代价最小。求最小代价。i-j放在一起的代价=(j-i+i到j的长度和-L)^2。
MY Solve:
首先难得自己推出了一次DP方程。。。。dp[i]表示前i个装好箱所需的最小代价。sum[i]很显然就是为了方便算一段连续物体的长度总和所记录的前缀和。
转移方程是这样的:
dp[i]=min(dp[j]+(i−j−1+sum[i]−sum[j]−L)2),(0<=j<i)
显然这样我O(n^2),T了。
然后开始优化吧。
我们定义:
f[i]=sum[i]+i,C=L+1
那么上式变成:
dp[i]=min(dp[j]+(f[i]−f[j]−C)2),(0<=j<i)
然后经过一系列不太复杂的转换发现它满足决策单调性—可以用斜率优化!
斜率优化详见 推荐博客
MY Code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define maxn 50010
#define ll long long
using namespace std;
int n,len;
int c[maxn];
ll sum[maxn];
ll dp[maxn];
ll q[maxn];
double slope(int j,int k){
return (dp[j]-dp[k]+(sum[j]+len)*(sum[j]+len)-(sum[k]+len)*(sum[k]+len))/(2.0*(sum[j]-sum[k]));
}
int main(){
scanf("%d%d",&n,&len);
memset(sum,0,sizeof sum);
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
sum[i]=sum[i-1]+c[i];
}
for(int i=1;i<=n;i++){
sum[i]+=i;
}
dp[0]=dp[1]=0;
int l=1,r=1;len++;
for(int i=1;i<=n;i++){
while(l<r && slope(q[l],q[l+1])<=sum[i])l++;
dp[i]=dp[q[l]]+(sum[i]-sum[q[l]]-len)*(sum[i]-sum[q[l]]-len);
while(l<r && slope(q[r-1],q[r])>slope(q[r],i))r--;
q[++r]=i;
}
printf("%lld\n",dp[n]);
return 0;
}