正题
题目链接:https://loj.ac/problem/2035
题目大意
个数字分成 段,要求方差最小。
解题思路
首先方差的公式
其中
是不变的,定义
设
表示已经分到第
段,到第
个时的最小方差和。
做前缀和
之后有
去掉
拆括号
求
最小就是
最小,后为了方便
定义
然后有若干个决策点
每次有一条直线
经过某个决策点要求
最小
显然因为
的单调性和
的单调性我们可以使用单调队列维护一个下凸壳。
时间复杂度
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pow2(x) ((x)*(x))
using namespace std;
const int N=3100;
struct node{
double x,y;
int num;
}q[N];
int n,m;
double s[N],f[N][N];
double slope(node x,node y)
{return (y.y-x.y)/(y.x-x.x);}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lf",&s[i]),s[i]=s[i]*m+s[i-1];
double w=s[n]/m;
for(int i=1;i<=n;i++)
f[1][i]=pow2(s[i]-w);
for(int k=2;k<=m;k++){
int head=1,tail=1;
q[1]=(node){s[k-1],f[k-1][k-1]+pow2(s[k-1]),k-1};
for(int i=k;i<=n;i++){
int z=2*(s[i]-w);
while(head<tail&&slope(q[head],q[head+1])<z)head++;
int p=q[head].num;
f[k][i]=f[k-1][p]+pow2(s[i]-s[p]-w);
node po=(node){s[i],f[k-1][i]+pow2(s[i]),i};
while(head<tail&&slope(po,q[tail])<slope(q[tail-1],q[tail]))
tail--;
q[++tail]=po;
}
}
printf("%.0lf",f[m][n]/m);
}