Description
Input
第一行为一个整数N表示战线的总长度。
第二行N个整数,第i个整数表示在位置i放置守卫塔的花费Ai。
Output
共一个整数,表示最小的战线花费值。
Sample Input
10
2 3 1 5 4 5 6 3 1 2
Sample Output
18
HINT
1<=N<=10^6,1<=Ai<=10^9
题解
斜率优化+dp
首先把序列反转,这样就是相当于1放堡垒然后全部向左看齐
朴素dp其实很容易想。。然而我一开始想错了
设f[i]表示第i个点放堡垒,1~i的最小花费
那么f[i]=min(f[j]+(i-j-1)*(i-j)/2+a[i])
之后一通优化就好了。。化式子在代码里有
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
int list[1110000],head,tail;
//在i点 对于k优于j的情况有
//f[j]+(i-j-1)*(i-j)/2 > (i-k-1)*(i-k)/2+f[k]
//设J=j+1 K=k+1
//2*f[j]+(i-J)(i-j) > 2*f[k]+(i-K)(i-k)
//2*f[j]+i*i-i*j-i*J+J*j > 2*f[k]+ i*i-i*k-i*K+K*k
//2*f[j]-i*j-i*J+J*j > 2*f[k]-i*k-i*K+K*k
// 2*f[k]-i*k-i*K+K*k < 2*f[j]-i*j-i*J+J*j
//2*(f[k]-f[j])+K*k-J*j < i*(k+K)- i*(j+J)
//2*(f[k]-f[j])+K*k-J*j < i*(k+K-j-J)
//2*(f[k]-f[j])+K*k-J*j/(k+K-j-J) < i
LL f[1110000];
LL a[1110000],n,ans;
double slop(LL k,LL j){return (2.0*(f[k]-f[j])+(k+1)*k-(j+1)*j)/(k+k+1-j-j-1);}
int main()
{
scanf("%d",&n);
memset(f,63,sizeof(f));
for(int i=1;i<=n;i++)scanf("%lld",&a[n-i+1]);
f[1]=a[1];head=tail=1;
list[1]=1LL;
ans=a[1]+1LL*n*(n-1)/2LL;
for(int i=2;i<=n;i++)
{
while(head<tail && slop(list[head+1],list[head])<i)head++;
f[i]=f[list[head]]+1LL*(i-list[head]-1)*(i-list[head])/2LL+a[i];
ans=min(ans,f[i]+1LL*(n-i+1)*(n-i)/2LL);
while(head<tail && slop(list[tail],list[tail-1])>slop(i,list[tail]))tail--;
list[++tail]=i;
}
printf("%lld\n",ans);
return 0;
}