BZOJ-1010&洛谷P3195玩具装箱toy-【HNOI2008】斜率优化DP+单调队列

Time Limit: 1 Sec  Memory Limit: 162 MB

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1010

洛谷:https://www.luogu.com.cn/problem/P3195

Description

  P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压
缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1...N的N件玩具,第i件玩具经过
压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容
器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一
个容器中,那么容器的长度将为 $x=j-i+sum_{k=i}^{j}C_{k}$ 制作容器的费用与容器的长度有关,根据教授研究,
如果容器长度为x,其制作费用为$(X-L)^{2}$.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容
器,甚至超过L。但他希望费用最小.

Input

  第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7

Output

  输出最小费用

Sample Input

5 4
3
4
2
1
4

Sample Output

1

emmm,我连DP都不会。。。突然让我写个斜率优化DP。。。
这个DP的式子不是很难得出来,当然对于我这种蒟蒻就很难了。设dp[i]为有i个玩具时的最优解,那么可得:
$dp[i]=min(dp[i],dp[j]+(i-j+sum[i]-sum[j]-L-1)^{2}$
即第i个最优解是由第j个最优解转移过来的,那么我们枚举j就完事了:
memset(dp,0x7f,sizeof dp);
dp[0]=0;
for (int i=1; i<=n; i++)
    for (int j=0; j<i; j++){
        dp[i]=min(dp[i],dp[j]+pow(sum[i]+i-sum[j]-j-m-1));
    }

而很显然,这是个$O(n^{2})$复杂度的算法。。。会T掉。加了$O_{2}$优化后在洛谷能得60分。而没有优化只有20分。。。

那么我们就需要将它优化成$O(nlogn)$或者$O(n)$的复杂度。

斜率优化的式子的一般是这样的:$dp[i]=a[i] imes b[j]+c[i]+d[j]$

至于这里的$dp[i]=dp[j]+(sum[i]+i-sum[j]-j-L-1)^{2}$  我们令$a[i]=sum[i]+i,b[i]=sum[i]+i+L+1$

则展开式子得:$dp[i]=dp[j]+a[i]^{2}+b[j]^{2}-2a[i]b[j]$ 那么可以知道的是j是在不断变动的,而i是固定的,那么我们可设b[j]为x,则式子可化为:

$dp[j]+b[j]^{2}=2a[i]b[j]-a[i]^{2}+dp[i]$即$Y=2a[i]X+B$

其中B包含dp[i],那么我们只要求Y在斜率为$2a[i]$下的最小截距就OK了,又$a[i]=sum[i]+i$为递增的,那么也就是说最优解的图形会构成一个下凸包的形状,

而显然,凸包中相邻两点的斜率是单调递增的,$2 imes a[i]$也是单调递增的,那么最优的点$P_{j}$即为第一个斜率$k(P_{j},P_{j+1})>2a[i]$的点。我们用单调队列维护这个凸包即最优点,设head和tail:
1.$while (k(q_{head},q_{head+1})<2a[i])  head++$
2.此时q[head]这个点为最优点,可直接算出dp[i]
3.$while (k(q_{tail-1},q_{tail})>k(q_{tail-1},i)) tail-- $ !维护凸包,新加入的点和tail-1的点构成的斜率必须小于tail和tail-1的斜率,这样才能构成下凸包:

如图所示,红色为新的点。

4.在队尾插入点$P_{i}$
 
更多更详细的斜率优化问题戳这里:https://www.cnblogs.com/Xing-Ling/p/11210179.html 
 
以下是AC代码: 
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

typedef long long ll;
const int mac=5e4+10;
int m;

ll dp[mac],q[mac],sum[mac];

ll X(ll x) {return sum[x];}

ll Y(ll x) {return dp[x]+(sum[x]+m)*(sum[x]+m);}

long double slope(ll a,ll b)
{
    return (long double)((Y(a)-Y(b))/(X(a)-X(b)));
}

int main()
{
    //freopen("in.txt","r",stdin);
    int n;
    scanf ("%d%d",&n,&m);
    for (int i=1; i<=n; i++){
        int x;
        scanf("%d",&x);
        sum[i]=sum[i-1]+x+1;
    }
    m++;
    int head=1,tail=0;
    q[++tail]=0;
    for (int i=1; i<=n; i++){
        while (head<tail && slope(q[head],q[head+1])<=2*sum[i])
            ++head;
        int j=q[head];
        dp[i]=dp[j]+(sum[i]-sum[j]-m)*(sum[i]-sum[j]-m);
        while (head<tail && slope(q[tail-1],q[tail])>=slope(q[tail-1],i))
            tail--;
        q[++tail]=i;
    }
    printf("%lld
",dp[n]);
    return 0;
}
 
 
 
 
路漫漫兮
原文地址:https://www.cnblogs.com/lonely-wind-/p/12205258.html