题目
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压
缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1…N的N件玩具,第i件玩具经过
压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容
器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一
个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,
如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容
器,甚至超过L。但他希望费用最小.
(Sigma指的是求和~)
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
思路:
这道题就是一道典型的斜率优化的dp问题,好好学习下~(o゚v゚)ノ
(本来打算自己写公式的,发现博客写的话好麻烦╮(╯▽╰)╭,以后学学)
然后我们看到最后一行式子,关键在于怎么选取j,来确定f[i]的最小值,这里我们用一个队列来维护,不断的向里面加入点,当然是有筛选的,先来看看加入新的点
假若以及加入了O,A,B三点,这个时候我们要加入C点,因为下一步就要判断C点这个位置的最小费用,也就是最小截距,可以看到A,C两点倘若相连,那么直线无论向上还是向下倾斜,在A,C两点的截距都小于B点(因为B点在上方嘛),然后我们就可以把B点删除了把C点加入,等下来就来算C点的最小费用,这里有个简单算法,也就是满足B,C结点的斜率大于A,B结点的斜率,否则一直删除结点(这里就维护了队尾)
然后我们再看到怎样取到最小费用对于每个点
好,我们看到这个图,当此时结点为D的时候,我们要计算这个最小截距,我们可以通过给出的函数,在队头一个一个计算,如果gettan越来越小,就一直删除队头,也就是head++,然后就可以得到此时的最小费用,因为可以通过上面的入队可以直到我们维护的是下凸包函数,斜率越来越大,所以算到这儿就可以了得到了(大概这个意思",ԾㅂԾ,")
(斜率优化,还是要自己去想想,拿起笔写一写的嘛)
代码如下:(有注释哦~(。・∀・)ノ)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mx=5e4+10;
int n,l;//分别为输入个数 和常数l
ll f[mx],s[mx];//分别为结果和 前缀和
int head,tail,h[mx];//分别为头,尾,每个数的前面加入的结点
ll gettan(int i,int j)//得到f[i];
{
return f[j]+s[j]*s[j]+(s[i]-l)*(s[i]-l)-2*s[j]*(s[i]-l);
}
bool cmp(int a,int b,int c)//比较斜率(s[x]就是自变量,f[x]+s[x]*s[x]为y)
{
return (f[b]+s[b]*s[b]-f[a]-s[a]*s[a])*(s[c]-s[b])>=(f[c]+s[c]*s[c]-f[b]-s[b]*s[b])*(s[b]-s[a]);
}
void slove()
{
head=tail=1;//用这个来模拟队列
h[tail++]=f[0]=0;
for(int i=1;i<=n;i++)
{
while(head+1<tail&&gettan(i,h[head])>=gettan(i,h[head+1]))
head++;//删除队列前面没有用的结点了
f[i]=gettan(i,h[head]);
while(head+1<tail&&cmp(h[tail-2],h[tail-1],i))
tail--;//一直删除旧结点,满足新节点插入时斜率变大
h[tail++]=i;//插入新的结点
}
}
int main()
{
int k;
scanf("%d%d",&n,&l);
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++)
{
scanf("%d",&k);
s[i]=s[i-1]+k;
}
l++;//式子变化
for(int i=1;i<=n;i++)//这个也是
s[i]+=i;
slove();
printf("%lld\n",f[n]);
return 0;
}