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;
}
posted @ 2017-02-16 16:47  v9fly  阅读(170)  评论(0编辑  收藏  举报