防御准备
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
Source
暴力就不多说了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
ll a[2000000];
ll f[2000000][3];
int main(){
int n;
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
f[n][1]=a[n];
f[n][0]=1LL<<60;
for (int i=n-1;i>=1;--i){
f[i][1]=a[i]+min(f[i+1][0],f[i+1][1]);
f[i][0]=1LL<<60;
for (int j=n;j>i;--j)
f[i][0]=min(f[i][0],f[j][1]+(1+j-i)*(j-i)/2);
//cout<<i<<" "<<f[i][0]<<" "<<f[i][1]<<endl;
}
printf("%lld\n",min(f[1][0],f[1][1]));
return 0;
}
斜率优化
设i<j<k
f[j][1]+(1+j-i)*(j-i)/2 < f[k][1]+(1+k-i)*(k-i)/2
i*2*(k-j)<2f[k][1]+k+k*k-2f[j][1]-j-j*j
令 check(j,k)=2f[k][1]+k+k*k-2f[j][1]-j-j*j / 2*(k-j)
所以
check(j,k)>i 时 (j比k优) 可以舍弃 k
ps:这里是倒序的!!!!!!
check(q[r],q[r-1])<check(i,q[r])
i<check(q[r],q[r-1])<check(i,q[r]) 时 i 最优
check(q[r],q[r-1])<i<check(i,q[r]) 时 i 或 q[r-1] 最优
check(q[r],q[r-1])<check(i,q[r])<i 时 q[r-1]最优
综上舍弃q[r]
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
ll a[2000000];
ll f[2000000][3];
ll q[2000000];
inline double check(ll j,ll k){
return (f[k][1]*2+k+k*k-f[j][1]*2-j-j*j)/2/(k-j);
}
int main(){
int n;
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
int l=1,r=1;
q[1]=n;
f[n][1]=a[n];
f[n][0]=1LL<<60;
for (int i=n-1;i>=1;--i){
f[i][1]=a[i]+min(f[i+1][0],f[i+1][1]);
while (l<r&&check(q[l+1],q[l])>i) ++l;
f[i][0]=f[q[l]][1]+(q[l]+1-i)*(q[l]-i)/2;
while (l<r&&check(q[r],q[r-1])>check(i,q[r])) --r;
++r;
q[r]=i;
//cout<<i<<" "<<f[i][0]<<" "<<f[i][1]<<endl;
}
printf("%lld\n",min(f[1][0],f[1][1]));
return 0;
}