P4072 [SDOI2016](BZOJ4518) 征途 [斜率优化DP]

题目描述

Pine开始了从S地到T地的征途。

从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。

Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。

Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。

帮助Pine求出最小方差是多少。

设方差是v,可以证明,v imes m^2v×m2是一个整数。为了避免精度误差,输出结果时输出v imes m^2v×m2。

输入格式

第一行两个数 n、m。

第二行 n 个数,表示 n 段路的长度

输出格式

一个数,最小方差乘以 m^2m2 后的值

输入输出样例

输入 #1
5 2
1 2 5 8 6
输出 #1
36

说明/提示

对于 30% 的数据,1n10

对于 60% 的数据,1n100

对于 100% 的数据,1n3000

保证从 S 到 T 的总路程不超过 30000 。


题解:题目意思是n段路每段路有一个长度,分m次走完,让你求这m次的方差乘以m^2的最小值。
 
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=30010;
int n,m,q[maxn];
ll f[maxn],g[maxn],sum[maxn],val;
inline ll X(int i){return sum[i];}
inline ll Y(int i){return (g[i]+sum[i]*sum[i]);}
inline double slope(int x,int y){return 1.0*(Y(y)-Y(x))/(X(y)-X(x));}


int main()
{
    scanf("%d%d",&n,&m); sum[0]=0;
    for(int i=1;i<=n;++i)
    {
        scanf("%lld",&val);
        sum[i]=sum[i-1]+val;
        g[i]=sum[i]*sum[i];
    }

    for(int i=1;i<m;++i)//m days
    {
        int l=0,r=0;q[0]=i;
        for(int j=i+1;j<=n;++j)// n roads
        {
            while(l<r&&slope(q[l],q[l+1])<2.0*sum[j])++l;
            f[j]=g[q[l]]+(sum[j]-sum[q[l]])*(sum[j]-sum[q[l]]);
            while(l<r&&slope(q[r-1],q[r])>slope(q[r-1],j)) --r;
            q[++r]=j;
        }
        for(int j=1;j<=n;++j) g[j]=f[j];
    }
    printf("%lld
",m*f[n]-sum[n]*sum[n]);

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/csushl/p/11381804.html