bzoj3156 防御准备(斜率)

Description
这里写图片描述
Input
第一行为一个整数N表示战线的总长度。
第二行N个整数,第i个整数表示在位置i放置守卫塔的花费Ai。

Output
共一个整数,表示最小的战线花费值。

Sample Input
10
2 3 1 5 4 5 6 3 1 2

Sample Output
18

分析:
斜率优化dp
方程:
f[i]=min{f[j]+(j+1~i-1每一个木偶到i的距离)}
括号里的贡献:
dis表示每个节点到1号店的距离
这里写图片描述
f[i]=min{f[j]+(i-j-1) * (i-1) - (i-j-1) * (j+i-2)/2+a[i]}

画柿子:
这里写图片描述

tip

千万不要画错柿子!!!

我在这里稍微总结一下可以使用斜率优化的一般条件:

  • 可以dp(其实就是具备最优子结构)
  • 一般是在序列上,一维二维都可以
  • 一般符合形式f[i]=f[j]+(i~j的贡献)
  • 可以画出柿子来,具备斜率的形式
这里写代码片
#include<cstdio>
#include<cstring>
#include<cstring>
#define ll long long

using namespace std;

const int N=1000010;
ll f[N],a[N];
int n,q[N],tou,wei;

double get(int j,int k)
{
    double x1=(double)f[j]+j-(j-j*j)/2;
    double x2=(double)f[k]+k-(k-k*k)/2;
    return (double)(x1-x2)/(j-k);
}

void doit()
{
    int i;
    tou=wei=0;
    for (i=1;i<=n;i++)
    {
        while (tou<wei&&get(q[tou+1],q[tou])<i) tou++;
        f[i]=f[q[tou]]+(i-q[tou]-1)*(i-1)-((i-q[tou]-1)*(q[tou]+i-2))/2+a[i];
        while (tou<wei&&get(i,q[wei])<get(q[wei],q[wei-1])) wei--;
        q[++wei]=i;
    }
    printf("%lld",f[n]);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    doit();
    return 0;
}
原文地址:https://www.cnblogs.com/wutongtong3117/p/7673142.html